C 언어 | 흐름 제어 | switch문

하나의 값에서 대응하는 복수의 코드 중에 하나를 선택하여 실행 switch 문을 소개한다.

다중 분기 판단

어떤 값의 상황에 따라 프로그램의 흐름을 변경하려면 if 문을 이용하여 해결할 수 있다. 분기의 수가 많은 경우는 if 문을 중첩된 if else if else ….라는 구조를 만들어서 구현할 수 있지만, 이것이 너무 많으면 if 문은 그다지 적합하지 않다.

예를 들어, 프로그램에 의미있는 수치를 메시지로 전달하고, 프로그램은 받은 값을 분석하고 적절한 처리를 실시하는 시스템을 실현하려면 메시지의 수는 엄청날 수 있다. 그래서 이런 프로그램을 실현하려면 switch 문을 사용하는 방법이 최적이라고 볼 수 있다.

switch문

switch (표현식) {
case 상수:
  블록문
default:
 디폴트 블록문
}

switch 문은 지정된 표현식을 판단해서 case에 지정된 상수와 일치하는 블록 문에서 실행을 시작한다. case는 복수 지정할 수 있으며 상수가 중복되어서는 안된다. default는 모든 case와 일치하지 않은 경우에 실행하는 특수 라벨이다. case에 상수만 지정할 수 있고, 변수는 지정할 수 없기 때문에 주의하도록 하자.

그런데 switch 문은 case와 default 뒤에 콜론:을 붙이는 이상한 구문을 가지고 있는데, 이것은 라벨 정의라는 구문에서, 그 직후 문장에 이름을 붙이기 위한 것이다. 라벨에 대한 자세한 내용은 goto 문으로 설명하겠지만, case와 default도 라벨의 일종이다.

라벨의 개념에 따라, 보다 정확한 switch 문의 성질을 해설하는 것이라면, 이것은 분기보다는 점프에 가까운 제어문이라고 생각된다. switch는 표현식을 판단하여 그 결과와 동일한 상수의 case 라벨을 가진 문장까지 이동한다. switch를 if 문 같은 분기문이라고 생각하고 사용하면, 아래 문장까지 실행(fall through)되는 현상으로 인해 버그가 될 수 있다. 다음과 같은 switch 문을 생각해 보자.

switch (0) {
case 0:
  printf("case 0");
case 1:
  printf("case 1");
default:
 printf("default");
}

이 switch 문을 실행하면 어떻게 될까? 식의 판단 부분에는 0이라는 상수를 지정한다. 이것은 case 0: 라벨과 일치하기 때문에 printf("case 0");가 실행된다는 것까지는 이해할 수 있는데, 생각치도 못하게 프로그램은 printf("case 1");printf("default");까지도 수행하고 마는 것이다. 이와 같이, 하단의 case와 default도 실행해 버리는 switch의 성질을 일반적으로 폴스루(Fall-through)라고 하는데, 이 결과에서 switch 문의 case는 if-else와는 달리, 단순한 문장의 라벨이 있는 것을 확인할 수 있다. switch 문은 표현식과 일치하는 case 라벨의 문장으로 점프할 뿐이다라는 개념을 이해하면, 어째서 그 후(다른 case와 default 다음)의 문장까지 실행되어 버리는지 납득할 수 있을 것이다.

그런데 대부분의 경우 아래 문장까지 실행되는 것은 바람직하지 않다. 가능하다면 목적의 case 라벨의 문장을 실행한 후의 switch를 벗어나 싶을 것이다. 여기에는 몇 가지 방법이 있지만, 가장 일반적인 것은 break 문을 사용하는 것이다. break는 이를 포함하는 가장 안쪽의 제어를 빠져 나오는 역할로써, switch와 루프를 벗어나기 위해 사용되는 키워드이다. 다음과 같이 기술하면, 특정 라벨을 실행한 후 빠져 나오게 된다.

switch (0) {
case 0:
 printf("case 0");
 break;
case 1:
 printf("case 1");
 break;
default:
  printf("default");
  break;
}

덧붙여서, case도 default도 선택적이며, 지정 위치는 임의이다. 일반적으로 default는 그 특성상, 하단 (마지막)로 지정되어 있지만, case 라벨이 default를 걸쳐도 문제는 없다. default가 생략된 상태에서 일치하는 case가 없으면 아무것도하지 않고 switch 문을 빠져 나온다.

코드1

#include <stdio.h>

int main() {
  int iSelected;
  printf("당신은, 귀여운 고양이를 만났다.\n");
  printf("0=머리를 쓰다듬는다, 1=꼬리를 만진다, 2=손가락을 내민다 >");
 scanf("%d" , &iSelected);

 switch(iSelected) {
 case 0:
   printf("반기는 듯하다.\n");
    break;
  case 1:
   printf("고양이가 싫어한다.\n");
    break;
  case 2:
   printf("손가가락의 냄새를 맡고 있다. 본능 같다.\n");
   break;
  default:
    printf("올바른 선택 번호를 입력하세요.\n");
 }
 return 0;
}

코드1은 간단한 분기 프로그램이다. 프로그램을 실행하면 문장이 표시되고 어떻게 행동 할 것인지를 선택하는 입력이 요구된다. 그 후에 프로그램은 입력된 값을 switch 문으로 판단하여 각 case문에에 분기한다. 적절한 문장을 표시시킨 후, 프로그램을 종료한다.

case에서 지정할 수 것은 상수이었지만, ASCII 코드는 1바이트의 수치로 표현할 수 있으므로, 1개의 문자 상수를 case로 지정할 수도 있다. 문자 상수를 지정하는 경우는 case 'A': 와 같이 기술한다.

코드2

#include <stdio.h>

int main() {
  char chSelected;
  printf("당신은, 귀여운 고양이를 만났다.\n");
  printf("A=머리를 쓰다듬는다, B=꼬리를 만진다, C=손가락을 내민다 >");
 scanf("%c" , &chSelected);

  switch(chSelected) {
  case 'A':
   printf("고양이가 싫어한다.\n");
    break;
  case 'B':
   printf("고양이가 싫어한다.\n");
    break;
  case 'C':
   printf("손가가락의 냄새를 맡고 있다. 본능 같다.\n");
   break;
  default:
    printf("올바른 선택 번호를 입력하세요.\n");
 }
 return 0;
}

이 프로그램은 코드1을 고쳐서 선택을 문자로 할 수 있도록 한 것이다.