switch 문
switch 문 (switch statement)
여러 분기 중에서, 어떤 조건 값에 따라 실행할 문장을 고르고 싶을 때 쓰는 게 switch 문이에요. 이 페이지에서는 switch 문의 문법과 조건, 레이블, 그리고 제어 흐름이 실제로 어떻게 이동하는지 정리할게요.
출처: cppreference
본문
switch 문은 조건의 값에 따라 여러 문장 중 하나로 제어를 옮겨요.
문법
attr(선택) switch ( init-statement(선택) condition ) statement |
| 항목 | 의미 | |
|---|---|---|
| attr | - | (C++11부터) 임의 개수의 속성 |
| init-statement | - | (C++17부터) 다음 중 하나 — 표현식 문(널 문 ;일 수도 있음) — 단순 선언(보통 이니셜라이저가 있는 변수 선언이지만, 임의 개수의 변수나 구조적 바인딩을 선언할 수 있음) — 별명 선언(C++23부터). 어떤 init-statement든 세미콜론으로 끝나야 한다는 점에 유의하세요. 그래서 흔히 비공식적으로 "표현식이나 선언 뒤에 세미콜론이 붙은 것"이라고 표현해요. |
| condition | - | 조건 |
| statement | - | 문장(보통 복합 문) |
조건 (Condition)
| 문법적으로 구조적 바인딩 선언으로 해석될 수 있으면, 구조적 바인딩 선언으로 해석돼요. | (C++26부터) |
|---|
- 문법적으로 표현식으로 해석될 수 있으면 표현식으로 다뤄져요. 그렇지 않으면 구조적 바인딩 선언이 아닌 선언으로 다뤄져요(C++26부터).
제어가 condition에 도달하면, condition은 값을 산출하고, 그 값으로 제어가 어느 레이블로 갈지 결정돼요.
표현식
condition이 표현식이면, 산출하는 값은 그 표현식의 값이에요.
선언
condition이 단순 선언이면, 산출하는 값은 결정 변수(아래 참고)의 값이에요.
비-구조적 바인딩 선언
선언은 다음 제한을 가져요:
- 문법적으로 다음 형태에 부합해야 해요:
- type-specifier-seq declarator
=assignment-expression (C++11 이전) - attribute-specifier-seq(선택) decl-specifier-seq declarator brace-or-equal-initializer (C++11부터)
- type-specifier-seq declarator
- declarator는 함수나 배열을 지정할 수 없어요.
- 타입 지정자 시퀀스(C++11 이전) / 선언 지정자 시퀀스(C++11부터)는 타입 지정자와
constexpr만 담을 수 있고, 클래스나 열거를 정의할 수 없어요.
선언의 결정 변수는 그 선언된 변수예요.
타입
condition은 다음 타입만 산출할 수 있어요:
- 정수 타입
- 열거 타입
- 클래스 타입
산출된 값이 클래스 타입이면, 맥락에 따라 암묵적으로 정수 또는 열거 타입으로 변환돼요.
(변환 후) 타입이 정수 승격의 대상이면, 산출된 값은 승격된 타입으로 변환돼요.
레이블 (Labels)
switch 문 안의 어떤 문장이라도 다음 레이블 중 하나 이상으로 레이블을 붙일 수 있어요:
attr(선택) case constant-expression : |
(1) |
attr(선택) default: |
(2) |
case 또는 default 레이블은 그 레이블을 둘러싸는 가장 안쪽 switch 문과 연결돼요.
다음 조건 중 하나라도 만족하면 프로그램은 ill-formed예요:
switch문에, 변환 후 같은 값을 갖는 constant-expression을 가진case레이블이 여러 개 연결된 경우.switch문에default레이블이 여러 개 연결된 경우.
제어 흐름 이동 (Control flow transfer)
switch 문의 조건이 (변환 후) 값을 산출하면:
- 연결된
case레이블 상수 중 하나가 같은 값이면, 매칭된case레이블이 붙은 문장으로 제어가 이동해요. - 그렇지 않고 연결된
default레이블이 있으면,default레이블이 붙은 문장으로 제어가 이동해요. - 그렇지 않으면,
switch문 안의 어떤 문장도 실행되지 않아요.
case와 default 레이블 자체는 제어 흐름을 바꾸지 않아요. switch 문 중간에서 빠져나가려면 break 문을 봐요.
컴파일러는 case 레이블 바로 앞에 [[fallthrough]] 속성이 와서 의도적인 낙하(fallthrough)임을 알리지 않는 한, 낙하(break 없이 다음 case·default 레이블에 도달)에 경고를 발행할 수 있어요(C++17부터).
switch (1)
{
case 1: std::cout << '1'; // "1"을 출력,
case 2: std::cout << '2'; // 그다음 "2"를 출력
}
switch (1)
{
case 1: std::cout << '1'; // "1"을 출력
break; // 그리고 switch를 빠져나감
case 2: std::cout << '2';
break;
}
### 이니셜라이저가 있는 switch 문 init-statement를 쓰면, switch 문은 다음과 같다: { init-statement switch ( condition ) statement } — 단, init-statement가 선언일 때 그 선언이 선언한 이름들과 condition이 선언일 때 그 선언이 선언한 이름들은 같은 범위에 있고, 그 범위가 statement의 범위이기도 한다. |
(C++17부터) |
|---|
주의할 점 (Notes)
제어 이동은 변수의 범위에 들어가는 것이 허용되지 않으므로, statement 안에서 선언 문을 만나면 그 선언은 자기 자신의 복합 문으로 범위를 감싸야 해요:
switch (1)
{
case 1:
int x = 0; // 초기화
std::cout << x << '\n';
break;
default:
// 컴파일 오류: default로 점프하면
// 'x'를 초기화하지 않고 그 범위에 들어가게 됨
std::cout << "default\n";
break;
}
switch (1)
{
case 1:
{
int x = 0;
std::cout << x << '\n';
break;
} // 'x'의 범위는 여기서 끝
default:
std::cout << "default\n"; // 오류 없음
break;
}
키워드
switch
예제
다음 코드는 switch 문의 여러 사용 사례를 보여줘요.
#include <iostream>
int main()
{
const int i = 2;
switch (i)
{
case 1:
std::cout << '1';
case 2: // 실행은 이 case 레이블에서 시작
std::cout << '2';
case 3:
std::cout << '3';
[[fallthrough]]; // C++17 속성: 낙하 경고를 끔
case 5:
std::cout << "45";
break; // 이후 문의 실행은 종료
case 6:
std::cout << '6';
}
std::cout << '\n';
switch (i)
{
case 4:
std::cout << 'a';
default:
std::cout << 'd'; // 적용 가능한 상수 표현식이 없으므로
// default가 실행됨
}
std::cout << '\n';
switch (i)
{
case 4:
std::cout << 'a'; // 아무것도 실행되지 않음
}
// switch 문에서 열거를 사용하면 많은 컴파일러가
// 열거자 중 하나가 처리되지 않으면 경고를 발행함
enum color { RED, GREEN, BLUE };
switch (RED)
{
case RED: std::cout << "red\n"; break;
case GREEN: std::cout << "green\n"; break;
case BLUE: std::cout << "blue\n"; break;
}
// 정수나 열거 타입으로의 암묵 변환이 없을 때
// C++17 init-statement 문법이 유용할 수 있음
struct Device
{
enum State { SLEEP, READY, BAD };
auto state() const { return m_state; }
/* ... */
private:
State m_state{};
};
switch (auto dev = Device{}; dev.state())
{
case Device::SLEEP: /* ... */ break;
case Device::READY: /* ... */ break;
case Device::BAD: /* ... */ break;
}
// 병리적 예제
// statement가 복합 문일 필요는 없음
switch (0)
std::cout << "this does nothing\n";
// 레이블도 복합 문을 요구하지 않음
switch (int n = 1)
case 0:
case 1: std::cout << n << '\n';
}
출력:
2345
d
red
1
i가 2라서 case 2:에서 실행이 시작되고, case 3:을 지나 [[fallthrough]]로 낙하가 의도적임을 알린 뒤 case 5:의 break까지 "2345"가 이어져 출력돼요. 두 번째 switch는 매칭되는 case가 없으니 default의 'd'가 출력되고, 세 번째는 아무것도 출력되지 않아요. enum color switch는 RED의 처리로 "red"가 나와요. 마지막 switch (int n = 1)은 이니셜라이저 문법과 case 레이블이 복합 문 없이도 가능하다는 걸 보여주며 n 값 1을 출력해요.
결함 보고 (Defect reports)
다음 동작 변경 결함 보고는 과거에 발표된 C++ 표준에 소급 적용됐어요.
(이 페이지에는 동작 변경 결함 보고가 없어요.)
더 알아보기
break문: switch 블록 중간에서 빠져나온다.[[fallthrough]](C++17): 이전 case 레이블에서의 낙하가 의도적임을 알리는 속성.