Chapter 7 Switch문

https://raw.githubusercontent.com/KuraiLuna/KuraiLuna.github.io/master/practical/C/%EC%94%B9%EC%96%B4%EB%A8%B9%EB%8A%94%20C%20%EC%96%B8%EC%96%B4/Chapter-07/Chapter-07-Switch%EB%AC%B8.gif

switch 문

번 기록에서는 if 문의 친구인

switch 문에 대해 배워 보도록 하겠다.

switch 문이 if문의 친구라고 한 이유는

하는 일이 정말로 if 문과 비슷하기 때문.

일단, 아래의 초-간단한

강아지 시뮬레이션을 보도록 하자.

/* 마이펫 */
#include <stdio.h>
  int main() {
    int input;

    printf("마이펫 \n");
    printf("무엇을 하실 것인지 입력하세요 \n");
    printf("1. 밥주기 \n");
    printf("2. 씻기기 \n");
    printf("3. 재우기 \n");

    scanf("%d", &input);

    if (input == 1) {
      printf("맛있어 \n");
  } else if (input == 2) {
      printf("시원 \n");
  } else if (input == 3) {
      printf("zzz \n");
  } else {
    printf("어 못 알아 듣겠다.  \n");
  }
  return 0;
}

성공적으로 컴파일 하였다면

 오후 8:15:29에 2021. 12. 13.에서 복원된 세션 콘텐츠 
새로운 크로스 플랫폼 PowerShell 사용 https://aka.ms/pscore6

PS D:\□□□□□_fFF□\□□□g□□□m□□□\F\> gcc -o test test.c
PS D:\□□□□□_fFF□\□□□g□□□m□□□\F\> ./test    
마이펫
무엇을 하실 것인지 입력하세요 
1. 밥주기
2. 씻기기 
3. 재우기 
4
무슨 명령인지 못 알아 듣겠어. 왈왈 
PS D:\□□□□□_fFF□\□□□g□□□m□□□\F\>

와 같이 3 가지 명령에

대해 반응하고 알 수 없는 명령은

’무슨 명령인지 못 알아 듣겠어. 왈왈’

라고 내보낸다.

그런데, 만약 강아지가 위 3 가지

명령만 반응하는 것이 아니라 10 가지

명령에 반응하게 하고 싶다고

해보도록 하자.

그렇다면 여러분은 아마도

아래와 같이 할 것.

(참고로 아래 ’…’ 인 부분은 필자가)

(쓰기 귀찮아서 생략한 부분 입니다.)

 if (…) {

 …

 } elseif (…) {

 …

 } elseif (…) {

 …

 } elseif (…) {

 …

 } elseif (…) {

 …

 } elseif (…) {

 …

 } elseif (…) {

 …

 } elseif (…) {

 …

 } elseif (…) {

 …

 } elseif (…) {

 …

 }

 

음, 아마도 위 소스코드를 보는

사람이 상당히 불편하게

느낄 것이라고 생각되지 않는가?

물론 보는 이에 따라 다르겠지만

아마 대부분의 사람이 그렇게 생각할 것.

아마 실제로 사람들과 함께 프로젝트를

진행 할 때 위와 같은 소스를 남발하게

된다면 읽는이도 불편하고 쓰는 사람도

손목이 많이 아플 것.

(물론 Ctrl + v 비기 가 있기는 하지만…)

따라서, 위와 같이 동일한 변수에

대해 비교문이 반복되는 경우에 아래와

같이 깔끔한 switch 문을 적용 시킬 수 있다.

/* 업그레이드 버전 */
#include <stdio.h>
int main() {
  int input;

  printf("마이펫 업그레이드\n");
  printf("--- 비둘기 ---\n");
  printf("무엇을 하실 것인지 입력하세요 \n");
  printf("1. 밥주기 \n");
  printf("2. 씻기기 \n");
  printf("3. 재우기 \n");

  scanf("%d", &input);

  switch (input) {
    case 1:
      printf("9-- 맛있쪙 \n");
      break;

    case 2:
      printf("9-- 개운--- \n");
      break;

    case 3:
      printf("zzz \n");
      break;

    default:
      printf("9--- 무슨 명령인지 못 알아 듣겠어. \n");
      break;
  }

  return 0;
}

아마 컴파일 된 결과는 위와

동일하게 나올 것. 이제, 위 소스

코드에서 가장 중요한 부분인

switch 문 부분을 살펴보도록 하자.

 switch (input) {

    case 1:

    printf(“9– 맛있쪙 \n);

    break;

     case 2:

     printf(“9– 개운— \n);

     break

     case 3:

     printf(“zzz \n);

     break;

     default:

     printf(9— 무슨 명령인지 못 알아 듣겠어. \n);

     break;

 }

 
 
switch 문의 기본 구조는 아래와 같다.
 

 switch (/* 변수 */) {

    case /* 값1 */:

    // 명령들;

      break;

    case /* 값2 */:

       // 명령들;

       break;

       //.. (생략) ..

 }

 
 
이 때, 변수 부분에는 값1, 값2, …

들과 비교할 변수가 들어가게 됩니다.

위 예제의 경우 input 을

1 과 2 와 3 과 비교해야 했으므로

변수 부분에는 input 이 들어가게 된다.

이 때 switch 문에 사용될 변수로는

반드시 정수 데이터를 보관하는 변수여야 한다.

다시말해 ’변수’ 부분에 들어가는 변수들의

타입은 char, short, int, long 중의

하나여야 한다.

만약 input 이 float 이나 double 이라면

컴파일시 오류가 발생되게 된다.

변수 == 값1 일 때, 가장 맨 위의

case 의 명령이 실행됩니다.

위 예제의 경우 1 이 입력되면 case 1:

이 참이 되므로 그 case 안의

내용들이 모두 실행된다.

이 때 각 명령들을 모두

실행한 후 break 를

만나면 switch 문을 빠져

나가게 된다.

예를 들어서 1 이 입력되었다면

case 1: 이 참이므로

printf(“9– 맛있쪙 \n”);

와 break; 가 실행되어

“9– 맛있쪙” 를 출력하고

break 를 통해 switch 문을 빠져 나가게 된다.

만약 변수 == 값2 라면 case 값1 은

실행되지 않고 case 값2 만 실행되게 된다.

또한 주의할 점으로는 ’값’ 에 위치하는

것들이 무조건 상수 이여야 한다는 것.

만약 ’값’ 부분에 변수들이 오게된다면

오류가 발생하게 되는데

그 이유는 switch 문의 내부적인

처리 방법 때문입니다.

(아래쪽 설명 되어 있다.)

마지막으로 switch 문의 default 는

if 문의 else 와 같은 역할을 합니다.

이도 저도 아닌 것들이 오는 case 이다.

즉 위 예제의 경우 input 이 1 도 2 도 3 도

아닐 때 도달하는 경우가 된다.

그런데 위 switch 문에서 등장한 break 는

어디서 많이 본 것 같지 않는가?

만약 그런 생각이 들었다면

당신은 C 언어 공부를 아주 충실히

하고 있다고 생각 됩니다.

break; 문을 실행하면 아래의 모든 case 들을

무시하고 switch 밖으로 빠져나가기 때문에

밥을 주었는데 강아지가 ’9– 맛있쪙’ 라고

할 일은 없게 된다. 하지만 만약

여러분이 break; 문을 빠뜨리게 되면

위와 같은 상황이 벌어질 수 있다.

/* 실패작 */
#include <stdio.h>
  int main() {
  int input;

  printf("마이펫 업그레이드\n");
  printf("무엇을 하실 것인지 입력하세요 \n");
  printf("1. 밥주기 \n");
  printf("2. 씻기기 \n");
  printf("3. 재우기 \n");

  scanf("%d", &input);

  switch (input) {
    case 1:
      printf("9-- 맛있쪙 \n");

    case 2:
      printf("9-- 개운--- \n");

    case 3:
      printf("zzz \n");

    default:
      printf("9--- 무슨 명령인지 못 알아 듣겠어. \n");
  }

  return 0;
}

성공적으로 컴파일 한다면

PS D:\□□□□□_fFF□\□□□g□□□m□□□\F\> gcc -o test test.c
PS D:\□□□□□_fFF□\□□□g□□□m□□□\F\> ./test
마이펫 업그레이드
무엇을 하실 것인지 입력하세요 
1. 밥주기
2. 씻기기
3. 재우기
1
9-- 맛있쪙 
9-- 개운---
zzz
9--- 무슨 명령인지 못 알아 듣겠어.
PS D:\□□□□□_fFF□\□□□g□□□m□□□\F\>

와 같이 웃지 않을 수 없는 상황이 벌어진다.

여러분들이 1 을 입력한다면 case 1: 이 실행되어

그 내용들이 모두 실행되지만 break 문으로

switch 문을 빠져 나가지 못해서 아래

case 들 까지 줄줄이 실행되어

위와 같은 꼴을 볼 수 있다.

/* 영어 말하기 */
#include <stdio.h>
  int main() {
    char input;

    printf("(소문자) 알파벳 읽기\n");
    printf("알파벳 : ");

    scanf("%c", &input);

    switch (input) {
      case 'a':
      printf("에이 \n");
      break;

      case 'b':
      printf("비 \n");
      break;

      case 'c':
      printf("씨 \n");
      break;

      default:
        printf("어... 어느정도 뒤에.. 말해줄려고 했는데 \n");
        printf("어... 못 읽겠어요 \n");
        break;
  }

  return 0;
}

성공적으로 컴파일 하였다면

PS D:\□□□□□_fFF□\□□□g□□□m□□□\F\> gcc -o test test.c
PS D:\□□□□□_fFF□\□□□g□□□m□□□\F\> ./test
(소문자) 알파벳 읽기
알파벳 : b
비
PS D:\□□□□□_fFF□\□□□g□□□m□□□\F\>

와 같이 나옵니다. 사실,

여기에 의문이 드는 사람들도 있다.

아까 위에서 switch 문은 정수

데이터만 처리한다고 했는데 왜

여기서는 문자 데이터도

처리가 되는 것인가?

그런데, 안타깝게도 이러한 의문이

5 초 이내로 해결되지 않으면

아마 앞에서 배운 내용을 까먹으셨을 것.

왜냐하면 컴퓨터는

문자와 숫자를 구분 못한다.

컴퓨터는 문자를 모두 숫자로 처리한 뒤,

우리에게 보여줄 때

에만 문자로 보여주는 것.

따라 서, 문자 = 정수 라고

생각해도 거의 무방하다.

이쯤 switch 문을 배우고 나면

드는 의문이 하나 있다.

“정말로 switch 문이

우리에게 필요한가?

if – else 로 다 해결되는데

왜 귀찮게 switch

문을 만들었을까?

 

차이는 단지 겉으로 얼마나

깔끔한지가 다를 뿐인데…

내부적으로 switch 문과

if-else 와는 차이가 없나요?”

정말로, 훌륭한 생각이라고 생각한다.

위 질문에 대한 답변을 정확하게 이해하려면

어셈블리어에 대한 이해가 필요로 하다.

(본인은 버퍼 오버플로우 취약점 공격 때문에)

(어셈블리어 도 같이 공부를 해야한다···)

(참고로 if 문과 switch 문의 차이에)

(대한 설명을 자세하게 잘 다루는 곳)

위에 링크 걸은 사이트에 들어가 내용을 깡그리

이해한다면 더할 나위 없이 좋겠으나

아마 C 언어를 처음 배우는 사람들의 경우

거의 이해를 못할 것이니 제가

간단하게 기록해 드리겠습니다.

(만약 아래 의 내용을 이해하지 못하더라고)

(그냥 넘어가도록 합니다.)

(사실 어셈블리어를 배우지 않은 이상)

(이해하기 매우 힘듭니다!)

https://raw.githubusercontent.com/KuraiLuna/KuraiLuna.github.io/master/practical/C/%EC%94%B9%EC%96%B4%EB%A8%B9%EB%8A%94%20C%20%EC%96%B8%EC%96%B4/Chapter-07/1.png
switch 문 이용!
 
https://raw.githubusercontent.com/KuraiLuna/KuraiLuna.github.io/master/practical/C/%EC%94%B9%EC%96%B4%EB%A8%B9%EB%8A%94%20C%20%EC%96%B8%EC%96%B4/Chapter-07/2.png
 

위 두 그림은 같은 소스

코드를 switch 문과 if

문을 이용하여 나타난 것. 사실,

외형적으로 동작하는

것은 차이가 없다.

단지 내부적으로 어떻게

처리되냐가 다를 뿐.

일단 if 문의 경우 각

경우 마다 값들을 비교 한다.

위 경우 값을 3 번 비교하겠네.

왜냐하면 if 가 1 번,

else if 가 2 번이고

else 의 경우 값의 비교 없이

자동으로 처리되는 것이므로

총 3 번 비교하게 된다.

즉, if 문을 이용하면

각 case 의 경우 비교하게

되므로 최악의 경우 모든 case 에

대해 값을 비교하는 연산

(어셈블리어에서는 CMP 연산을 한다.)

을 시행하게 된다.

그런데 switch 문은 사뭇 다르다.

switch 의 경우 내부적으로

jump table 이라는 것을 생성합니다.

이 때, jump table 의 크기는 case 의

값들에 따라 달라지는데,

예를 들어서 어떤 switch 문의 경우

case 1: ∼ case 10: 까지 있었다고 합시다.

그렇다면 jump table 에는 값들이

0 부터 9 까지 들어가게 된다.

여기서 우리는 왜 case 값: 할 때,

’값’ 부분에 변수가 위치하면

안되는지 알게 된다.

jump table 은 프로그램

초기에 작성 되기 때문에

이미 switch 문이 실행되기 전에

jump table 이 작성되게 된다.

따라서, ’값’ 부분에 변수가

들어가게 되면

jump table 에 무엇이

올지 알 수 없으므로

변수를 사용하면 안되는 것.

이 값들은 무엇을 의미하냐면

각 case 별로

명령들이 위치한 곳의

주소를 가리키는데

예를 들어서 1 인

지점으로 점프하게 되면

“9– 맛있쪙” 가 나오고

0 인 지점으로

점프하게 되면

“9– 맛있쪙”

라고 출력하라는

내용의 명령문들이

나온다. 이제,

변수의 값에 따라 변수가 3 이라면

jump table 의 3 번째 원소를 찾아서

그 값에 해당하는 곳으로 점프하게 된다.

(실제로 switch 문이 처리되는 과정은)

(이보다 약간 더 복잡하지만)

(어셈블리어를 배우지 않은)

(현재 상황으로써는)

(최선이라 생각됩니다)

따라서, switch 문을 이용하면

case 에 따라 CMP 연산이

늘어나는 것이 아니라

jump table 의 크기만 커질 뿐

성능에 있어서는 전혀 영향을

받지 않게 된다.

결론적으로 이야기 하자면 switch

문이 효과적으로 처리되기 위해서는

case 의 ’값’ 들의 크기가 그다지

크지 않아야 하고, ’값’ 들이 순차적으로

정렬되어 있고, 그 ’값’ 끼리의

차이가 크지 않다면 최고로 효율적인

switch 문을 이용할 수 있게 된다.

 정 리

  • 어떤 정수 변수에 대해서 반복적으로 사용하는 if-else 문이

    있다면 switch 를 사용하 면 더 깔끔하게 바꿀 수 있습니다.

  • 각 case 문 안에서 적절히 break 하는 것을 빠뜨리면 안됩니다.

  • default 를 사용하면 else 문과 같은 효과를 낼 수 있습니다.

 
 
 
 
https://raw.githubusercontent.com/KuraiLuna/KuraiLuna.github.io/master/practical/C/%EC%94%B9%EC%96%B4%EB%A8%B9%EB%8A%94%20C%20%EC%96%B8%EC%96%B4/Chapter-07/%EC%98%A4%EB%8A%98%EB%8F%84_%EC%A2%8B%EC%9D%80_%ED%95%98%EB%A3%A8.png