소년코딩

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 문 내부에서 casedefault 레이블 전후에 변수를 선언할 수는 있지만, 초기화할 수는 없다.

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;
}

변수 ycase 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;
}

cpp 번역: 이 포스트의 원문은 http://www.learncpp.com/cpp-tutorial/53-switch-statements/ 입니다.

댓글 로드 중…

블로그 정보

소년코딩 - 소년코딩

소년코딩, 자바스크립트, C++, 물리, 게임 코딩 이야기

최근에 게시된 이야기