06.02 - switch 문 (switch statement)
많은 if-else
문을 연결할 수 있지만, 이것은 가독성이 떨어져 읽기 어렵다. 아래 프로그램을 보자.
#include <iostream>
enum Colors
{
COLOR_BLACK,
COLOR_WHITE,
COLOR_RED,
COLOR_GREEN,
COLOR_BLUE
};
void printColor(Colors color)
{
if (color == COLOR_BLACK)
std::cout << "Black";
else if (color == COLOR_WHITE)
std::cout << "White";
else if (color == COLOR_RED)
std::cout << "Red";
else if (color == COLOR_GREEN)
std::cout << "Green";
else if (color == COLOR_BLUE)
std::cout << "Blue";
else
std::cout << "Unknown";
}
int main()
{
printColor(COLOR_GREEN);
return 0;
}
같음을 테스트하는 단일 변수에서 if-else
체인을 수행하는 것이 일반적이므로 C++은 switch
라는 조건부 분기 문을 제공한다. 위와 같은 프로그램의 switch
문 형태를 소개한다.
void printColor(Colors color)
{
switch (color)
{
case COLOR_BLACK:
std::cout << "Black";
break;
case COLOR_WHITE:
std::cout << "White";
break;
case COLOR_RED:
std::cout << "Red";
break;
case COLOR_GREEN:
std::cout << "Green";
break;
case COLOR_BLUE:
std::cout << "Blue";
break;
default:
std::cout << "Unknown";
break;
}
}
switch
문에 대한 개념은 간단하다. switch
표현식이 값을 평가하고, 각 case
레이블의 값과 '같은' 지 테스트한다. 레이블의 값과 같으면 레이블 뒤의 명령문을 실행한다. 일치하는 레이블이 없으면 default
뒤의 명령문을 실행한다. (존재하는 경우)
이러한 방식으로 구현되기 때문에 switch
문이 if-else
체인보다 일반적으로 더 효율적이다.
Starting a switch
switch
키워드를 사용하여 switch
문을 시작한 다음 평가한 표현식을 사용한다. 일반적으로 이 표현식은 단 하나의 변수이지만 nx + 2 또는 nx - ny와 같이 좀 더 복잡할 수 있다. 이 표현식에 대한 한 가지 제한 사항은 정수 유형(char, short, int, long, long long, enum)으로 평가되어야 한다는 것이다. 부동 소수점 변수 및 기타 비 정수 유형은 표현식으로 사용할 수 없다.
switch
식 뒤에 블록을 선언한다. 블록 안에는 레이블(label)을 사용하여 '같음'을 테스트하려는 모든 값을 정의한다. 레이블에는 두 가지 종류가 있다.
switch (expression)
{
// '같음'을 테스트하는 레이블이 들어간다.
}
Case labels
첫 번째 종류의 레이블은 case
레이블이다. case
키워드를 사용하여 선언된 다음 상수 표현식이 온다. 상수 표현식은 상수값을 평가하는 표현식이다. 즉, 리터럴(Ex. 5), 열거형(Ex. COLOR_REN) 또는 상수 변수(Ex. const int 자료형인 변수 x) 등이다.
case
레이블 다음의 상수 표현식은 switch
키워드 다음의 표현식과 평가된 값이 '같은'지 테스트 된다. 같을 경우, case
레이블 아래의 코드가 실행된다.
주의: 모든 case
레이블 표현식은 고유한 값으로 평가되어야 한다.
switch (x)
{
// 주의: 모든 case 레이블 표현식은 고유한 값으로 평가되어야 한다.
case 4:
case 4: // illegal -- already used value 4!
case COLOR_BLUE: // illegal, COLOR_BLUE evaluates to 4!
};
여러 case
레이블이 같은 명령문을 참조하도록 할 수 있다. 다음 함수는 여러 case
를 사용하여 c
매개 변수가 ASCII 숫자인지 테스트하는 기능을 가진다.
bool isDigit(char c)
{
switch (c)
{
case '0': // if c is 0
case '1': // or if c is 1
case '2': // or if c is 2
case '3': // or if c is 3
case '4': // or if c is 4
case '5': // or if c is 5
case '6': // or if c is 6
case '7': // or if c is 7
case '8': // or if c is 8
case '9': // or if c is 9
// 같은 명령문을 참조한다.
return true; // then return true
default:
return false;
}
}
위 함수는 c
가 ASCII 숫자인 경우, 일치하는 case
문 다음 첫 번째 명령문이 실행된다. (즉, return true;
)
The default label
두 번째 종류의 레이블은 default
레이블이다. (defualt case라고도 불린다.) default
키워드를 사용하여 선언된다. 이 레이블 아래의 코드는 switch
표현식과 일치하는 case가 하나도 없으면 실행된다. default
레이블은 선택사항이며 switch
문당 하나만 있을 수 있다. 일반적으로 switch
문 블록의 마지막 레이블로 선언되지만, 반드시 필요한 것은 아니다.
위 isDisit()
함수 예제에서 c가 ASCII 숫자가 아닌 경우 default case가 실행되고 false를 반환한다.
Switch execution and fall-through
switch
문의 주의점 중 하나는 switch
표현식의 값과 일치 할 때 실행이 진행되는 방식이다. 값이 일치하는 case
레이블을 만나거나 default case 문이 실행되면 해당 레이블 다음의 첫 번째 명령문에서 실행이 시작되고, 다음과 같은 종료 조건 중 하나가 충족 될 때까지 실행이 계속된다.
switch
블록의 끝에 도달할 경우
return
문이 발생할 경우
goto
문이 발생할 겨우
break
문이 발생할 경우
- 뭔가 다른 것이 프로그램의 정상적인 흐름을 방해할 경우 (Ex. exit() 호출, 예외 발생 등)
이러한 종료 조건 중 하나가 충족되지 않으면 case 문은 계속해서 후속 case 문으로 넘어간다.
switch (2)
{
case 1: // Does not match
std::cout << 1 << '\n'; // skipped
case 2: // Match!
std::cout << 2 << '\n'; // Execution begins here
case 3:
std::cout << 3 << '\n'; // This is also executed
case 4:
std::cout << 4 << '\n'; // This is also executed
default:
std::cout << 5 << '\n'; // This is also executed
}
result:
2
3
4
5
실행 흐름이 한 case에서 다른 case로 넘어가는 경우를 fall-through라고 한다. fall-through는 의도적인 경우가 거의 없으므로 주의해야 한다.
fall-through는 '구멍 사이로 떨어지다'에서 유래한 말이다.
Break statements
break
명령문(break
키워드를 사용하여 선언)은 컴파일러에 이 switch
문 사용이 완료되었음을 알려준다.( 또는 while, do while, for 루프) break
문이 발생하면 switch
블록이 끝난다.
break
문이 제대로 삽입된 예제를 보자.
switch (2)
{
case 1: // Does not match -- skipped
std::cout << 1 << '\n';
break;
case 2: // Match! Execution begins at the next statement
std::cout << 2 << '\n'; // Execution begins here
break; // Break terminates the switch statement
case 3:
std::cout << 3 << '\n';
break;
case 4:
std::cout << 4 << '\n';
break;
default:
std::cout << 5 << '\n';
break;
}
// Execution resumes here
2
이제 case 2:
가 일치하면 정수 2가 출력되고, break
명령문으로 인해 switch
문이 종료된다.
경고: C++에서 case 문 끝에 break를 잊어 버리는 경우는 가장 흔한 오류중 하나다!
Variable declaration and initialization inside case statements
switch
문 내부에서 case
와 default
레이블 전후에 변수를 선언할 수는 있지만, 초기화할 수는 없다.
switch (1)
{
int a; // okay, declaration is allowed before the case labels
int b = 5; // illegal, initialization is not allowed before the case labels
case 1:
int y; // okay, declaration is allowed within a case
y = 4; // okay, this is an assignment
break;
case 2:
y = 5; // okay, y was declared above, so we can use it here too
break;
case 3:
int z = 4; // illegal, initialization is not allowed within a case
break;
default:
std::cout << "default case" << std::endl;
break;
}
변수 y
는 case 1:
레이블에서 정의되어 있지만, case 2:
에서도 변수 y
에 접근할 수 있다. 레이블의 명령문은 암시적으로 모두 같은 범위(scope)에 속하게 된다. 따라서 변수가 정의된 레이블이 실행되지 않더라도 다른 레이블에서 사용할 수 있다.
int y
와 같은 지역 변수를 정의하면 변수는 그 시점에서 생성되지 않는다. 실제로 선언된 블록의 시작 부분에서 생성된다. 따라서 변수를 선언하는 case
문이 실행되지 않더라도 다른 case
문에서 사용할 수 있다.
레이블 아래에서 변수의 초기화는 허용되지 않고 컴파일 오류가 발생한다. 변수를 초기화할 때 실행이 필요하지만, 초기화가 포함된 case
문이 실행되지 않을 수 있기 때문이다!
만약 case
문에서 새 변수를 정의 및 초기화해야 하고 싶다면, case
문 아래에 있는 블록 내에서 정의 및 초기화를 하면 된다.
switch (1)
{
case 1:
{ // note addition of block here
int x = 4; // okay, variables can be initialized inside a block inside a case
std::cout << x;
break;
}
default:
std::cout << "default case" << std::endl;
break;
}
번역: 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/53-switch-statements/ 입니다.