08.1 왜 if 대신 switch를 쓰는가

값 하나를 여러 후보와 차례로 견주는 상황은 프로그램에서 아주 흔합니다. 메뉴 번호가 1인지 2인지, 학점이 A인지 B인지 같은 경우입니다. 이런 갈림길을 ifelse if로만 짜면 같은 변수 이름을 몇 번이고 다시 적게 됩니다.


if (menu == 1) printf("주문\n");

else if (menu == 2) printf("결제\n");

else if (menu == 3) printf("취소\n");

else printf("잘못된 선택\n");


menu == 이 반복이 눈에 거슬립니다. 비교 대상이 하나로 정해져 있고 그 값에 따라서만 갈린다면, switch가 같은 일을 더 또렷하게 표현합니다. 무엇을 기준으로 가르는지가 맨 윗줄에 드러나기 때문입니다.

08.2 switch의 기본 생김새

switch는 괄호 안에 넣은 값 하나를, 아래 늘어놓은 여러 case(경우)의 상수와 위에서부터 맞대어 봅니다. 일치하는 case를 찾으면 그 자리로 뛰어들어 코드를 실행하고, break(끊고 빠져나감)를 만나면 switch 전체를 벗어납니다. 어느 case와도 맞지 않으면 default(해당 없음)로 갑니다.


switch (값) {

case 상수1:

실행할 문장;

break;

case 상수2:

실행할 문장;

break;

default:

실행할 문장;

}


[C 08] 조건문 switch로 여러 갈래 나누기


08.3 메뉴 번호로 갈라지기

가장 흔한 쓰임인 메뉴 선택부터 따라 쳐 봅니다. 변수 menu에 담긴 번호에 맞는 안내만 출력됩니다.


#include <stdio.h>


int main(void) {

int menu = 2;

switch (menu) {

case 1: printf("주문\n"); break;

case 2: printf("결제\n"); break;

case 3: printf("취소\n"); break;

default: printf("잘못된 선택\n");

}

return 0;

}


실행하면 다음이 출력됩니다.


결제


menu가 2이므로 case 2로 뛰어들어 결제를 찍고, 곧바로 만난 breakswitch를 빠져나옵니다. 한 줄에 몰아 적든 여러 줄로 풀어쓰든 동작은 똑같습니다.

08.4 break를 빠뜨리면 벌어지는 일

함정. switch에서 break를 빼먹는 것은 초보자가 가장 자주 밟는 지뢰입니다. case는 "여기로 뛰어들라"는 표지일 뿐 "여기서 멈추라"는 뜻이 아닙니다. 그래서 break가 없으면 일치한 case부터 그 아래의 case 표지들을 무시하고 문장을 계속 실행합니다. 이렇게 아래로 주르륵 흘러내리는 현상을 폴스루(fall-through)라고 부릅니다.


int menu = 1;

switch (menu) {

case 1: printf("주문\n");

case 2: printf("결제\n");

case 3: printf("취소\n");

default: printf("잘못된 선택\n");

}


break를 모두 뺐더니 출력은 이렇게 됩니다.


주문

결제

취소

잘못된 선택


menu가 1이라 case 1로 들어간 것까지는 맞지만, 멈추지 못하고 아래 문장을 전부 실행해 버린 것입니다. 원하는 갈래 하나만 고르려면 각 case의 끝에 break를 반드시 붙여야 합니다. 맨 마지막 default는 뒤에 실행할 것이 없어 break를 생략해도 됩니다.

08.5 일부러 흘려보내 case 묶기

폴스루가 늘 실수인 것은 아닙니다. 여러 case를 같은 동작으로 묶고 싶을 때는 일부러 break 없이 case를 잇따라 적습니다. 요일 번호를 평일과 주말로 가르는 예를 봅니다.


int day = 6; /* 1~5 평일, 6~7 주말 */

switch (day) {

case 1: case 2: case 3: case 4: case 5:

printf("평일\n");

break;

case 6: case 7:

printf("주말\n");

break;

default:

printf("잘못된 요일\n");

}


출력은 다음과 같습니다.


주말


case 1부터 case 5까지는 사이에 break가 없어 어느 번호로 들어오든 똑같이 평일을 찍고 하나의 break에서 함께 빠져나옵니다. 값이 6이면 case 6으로 들어가 주말이 출력됩니다.

08.6 문자로도 나눌 수 있다

switch가 견주는 값은 정수뿐 아니라 문자도 됩니다. 문자는 속으로 숫자(아스키 코드)로 다뤄지기 때문입니다. 문자 상수는 작은따옴표로 'A'처럼 적습니다.


char grade = 'B';

switch (grade) {

case 'A': printf("훌륭해요\n"); break;

case 'B': printf("잘했어요\n"); break;

case 'C': printf("보통이에요\n"); break;

default: printf("분발하세요\n");

}


출력은 다음과 같습니다.


잘했어요


grade'B'이므로 case 'B'가 골라집니다. 큰따옴표로 묶는 문자열 "B"case에 올 수 없습니다. 문자열은 값 하나로 견줄 수 없어, 그럴 땐 switch 대신 strcmp 같은 비교 함수를 씁니다.

08.7 연산 기호로 만드는 미니 계산기

두 수와 연산 기호를 받아 계산하는 작은 계산기도 switch로 깔끔하게 짤 수 있습니다. 연산 기호 op는 문자 '+', '-', '*', '/' 중 하나입니다.


int a = 6, b = 2;

char op = '*';

switch (op) {

case '+': printf("%d\n", a + b); break;

case '-': printf("%d\n", a - b); break;

case '*': printf("%d\n", a * b); break;

case '/':

if (b != 0) printf("%d\n", a / b);

else printf("0으로 못 나눔\n");

break;

default: printf("알 수 없는 연산\n");

}


출력은 다음과 같습니다.


12


op가 곱셈 기호라 case '*'가 실행돼 12가 나옵니다. 나눗셈 갈래에서는 case 안에 if를 겹쳐 0으로 나누는 사고를 막았습니다. 주의. 정수를 0으로 나누면 프로그램이 그 자리에서 비정상 종료하는 정의되지 않은 동작(UB, undefined behavior)이므로, 나눗셈 앞에서는 나누는 값이 0인지 늘 확인해야 합니다.

08.8 switch가 못 하는 것과 default의 쓸모

switch는 만능이 아닙니다. 오직 "값이 이 상수와 정확히 같은가"만 따질 수 있습니다. 주의. 점수가 90 이상이면 A, 80 이상이면 B처럼 범위로 가르는 판정은 switch로 짤 수 없고 ifelse if를 써야 합니다. case (score >= 90) 같은 조건식은 case 자리에 올 수 없기 때문입니다. 또 case 뒤에는 컴파일 시점에 정해진 상수만 와야 하며, 값이 바뀌는 변수는 넣을 수 없습니다.

switch는 하나의 정수나 문자를 정해진 값들과 대조해 여러 갈래로 나눌 때 가장 빛납니다. default는 어느 값에도 걸리지 않은 나머지를 받아 내는 그물이니, 예상 밖 입력을 놓치지 않으려면 웬만하면 넣어 두는 편이 좋습니다.