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 (/* 변수 */) { case /* 값1 */: // 명령들; break; case /* 값2 */: // 명령들; break; //.. (생략) .. } |
들과 비교할 변수가 들어가게 됩니다.
위 예제의 경우 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 언어를 처음 배우는 사람들의 경우
거의 이해를 못할 것이니 제가
간단하게 기록해 드리겠습니다.
(만약 아래 의 내용을 이해하지 못하더라고)
(그냥 넘어가도록 합니다.)
(사실 어셈블리어를 배우지 않은 이상)
(이해하기 매우 힘듭니다!)
위 두 그림은 같은 소스
코드를 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 문과 같은 효과를 낼 수 있습니다. |