조건문 switch
switch가 정수·열거형 조건과 case 레이블을 선택하는 흐름을 익히고 break·fall-through 및 if문과의 선택 기준을 이해합니다.
지난 장에서 if, else if, else 문을 통해 조건에 따라 프로그램의 흐름을 제어하는 방법을 학습했습니다.
이 문법은 매우 유연하고 강력하지만, 하나의 변수 값이 여러 가지 경우의 수에 따라 각각 다른 동작을 해야 할 때는 else if를 길게 나열해야 하는 경우가 발생합니다.
예를 들어 사용자 입력에 따라 요일을 판별하거나, 메뉴 선택에 따라 다른 기능을 실행해야 하는 경우입니다.
이런 상황에서 같은 값에 대한 분기를 레이블 중심으로 정리할 수 있는 대안이 바로 switch 문입니다.
switch 문은 하나의 변수(또는 표현식)의 값에 따라 여러 case 중 하나를 선택하여 실행하는 조건문입니다.
ONE VALUE · CONSTANT LABELS
switch는 임의의 Boolean 조건 목록이 아닙니다. 조건을 평가하고 필요한 변환·승격을 거친 하나의 값을 같은 타입으로 변환 가능한 case 상수들과 맞춥니다.
| # | 검사 지점 | C++ 계약 | 선택 기준 |
|---|---|---|---|
| 1 | 조건 타입 | 정수형·열거형, 또는 그 타입으로 문맥상 암시적 변환 가능한 클래스 타입입니다. 적용 대상이면 정수 승격 뒤의 타입이 기준이 됩니다. | char, 작은 정수, bool, 열거형은 가능하지만 실수와 std::string은 직접 사용할 수 없습니다. |
| 2 | case 값 |
조정된 조건 타입의 변환된 상수식이어야 하며, 변환 뒤 같은 값을 갖는 레이블을 둘 이상 둘 수 없습니다. | 런타임 변수, 범위식, 동일 값으로 변환되는 별칭 레이블은 사용할 수 없습니다. |
| 3 | 선택 | 조건은 switch 진입마다 한 번 평가됩니다. 값이 맞는 case가 시작점이고, 없으면 default, 그것도 없으면 본문을 건너뜁니다. |
default는 선택 사항이며 한 개만 둘 수 있습니다. |
| 4 | if가 맞는 경우 |
범위, 실수·문자열 비교, 여러 값의 논리 조합처럼 각 분기가 서로 다른 조건식을 요구할 때입니다. | 문법만으로 성능 우위를 단정하지 말고 조건의 모양과 읽기 쉬운 구조를 기준으로 고릅니다. |
- 1 · 조건 값
- 정수형·열거형 또는 그 타입으로 문맥상 변환 가능한 클래스 타입만 허용됩니다. 필요한 정수 승격 뒤 타입이 비교 기준입니다.
- 2 ·
case상수 - 조정된 조건 타입의 변환된 상수식이어야 하며, 변환 결과가 같은 레이블은 중복입니다.
- 3 · 한 번 평가하고 선택
- 일치한
case, 없으면default, 둘 다 없으면 본문 밖으로 진행합니다. - 4 · 범위·문자열·복합 논리
- 서로 다른 조건식을 조합해야 하면
if체인이 자연스럽습니다. 성능은 측정과 구현에 달려 있습니다.
switch의 강점은 하나의 값과 고정 레이블 목록이라는 의도를 드러내는 데 있습니다. 컴파일러는 switch와 if 모두를 다양한 방식으로 최적화할 수 있습니다.switch 문이란 무엇인가?
switch 문은 주어진 표현식의 값을 평가하여 그 값과 일치하는 case 레이블로 프로그램의 실행 흐름을 점프시킵니다.
여러 개의 가능한 값 중 하나를 선택하여 처리할 때 특히 유용합니다.
switch 문을 사용하는 경우
- 하나의 정수형·열거형 값이 여러 개의 특정 값 중 하나일 때.
- 다수의
else if문이 동일한 변수에 대한 동등 비교(==)를 반복하는 경우.
switch 문 제약사항
switch문의 조건은 정수형·열거형이거나 그 타입으로 문맥상 변환되는 클래스 타입이어야 합니다. 조건에는 필요한 정수 승격이 적용됩니다. 실수형(float,double)이나 문자열(std::string)은 직접 사용할 수 없습니다.- 각
case값은 조정된 조건 타입의 변환된 상수 표현식이어야 합니다.constexpr처럼 컴파일 시점에 값이 정해지는 표현식은 사용할 수 있지만 런타임 변수는 사용할 수 없으며, 조건 타입으로 변환한 결과가 같은case도 중복입니다.
switch는 범위 조건을 처리하는 도구가 아니라, 하나의 정수형 또는 열거형 값이 어떤 상수 레이블에 맞는지 고르는 구조입니다.
switch 문 형식 및 기본 사용법
switch 문의 기본적인 형식은 다음과 같습니다.
switch (표현식) {
case 상수값1:
// 표현식의 값이 상수값1과 같을 때 실행할 코드
break; // 보통 각 case의 실행을 여기서 끝냄
case 상수값2:
// 표현식의 값이 상수값2와 같을 때 실행할 코드
break;
case 상수값3:
// 표현식의 값이 상수값3과 같을 때 실행할 코드
break;
default: // 선택 사항
// 위의 어떤 case에도 해당하지 않을 때 실행할 코드
break; // default도 보통 break를 사용 (습관화)
}switch (표현식): 괄호 안의 표현식을 한 번 평가하고, 필요한 변환과 정수 승격을 적용한 값으로 진입 레이블을 고릅니다.case 상수값:case키워드 뒤에 조건과 맞출 상수값을 적고 콜론(:)을 붙입니다. 변환 후 같은 값인 레이블을 둘 이상 선언할 수 없습니다.break;: 자신을 둘러싼 가장 안쪽 반복문 또는switch를 즉시 종료하고 그 다음 문장으로 실행 흐름을 넘깁니다. 중첩 반복문이 없는case본문에서는 현재switch를 끝냅니다. 문법상 모든 레이블 끝에 필수인 것은 아니므로, 이어서 실행할 의도가 없다면 명시하는 종료 정책으로 이해해야 합니다.default:: 선택 사항이며 최대 하나만 둘 수 있습니다. 어떤case와도 일치하지 않을 때 이 레이블부터 실행하고,default도 자동으로 실행을 멈추지 않습니다. 어느 위치에나 둘 수 있지만 흐름을 쉽게 읽도록 보통 마지막에 둡니다.
아래 다이어그램은 switch 문이 표현식을 평가한 뒤 case 또는 default로 흐름을 옮기고, break를 만나 다음 코드로 빠져나가는 과정을 단계별로 보여줍니다.
SELECT ENTRY · THEN EXECUTE
case와 default는 독립된 블록이나 자동 정지선이 아니라 레이블입니다. 시작점을 고른 뒤에는 점프문을 만나거나 switch 본문이 끝날 때까지 문장이 순서대로 실행됩니다.
-
조건을 한 번 평가하고 조정된 값으로 만든다
switch (grade)진입 시 조건을 평가하고 필요한 변환·정수 승격을 적용합니다. -
일치한 레이블을 실행 시작점으로 고른다
같은 값의
case가 있으면 그 뒤 문장으로 이동합니다. 없으면default, 그것도 없으면 본문을 실행하지 않습니다. -
다음 레이블도 가로질러 순서대로 실행한다
레이블 자체는 흐름을 멈추지 않습니다.
grade가'B'라서 그 레이블에서 시작하고break가 없다면case 'C'아래 문장도 실행됩니다. -
종료 또는 의도한 이어짐을 코드에 표시한다
break는 자신을 둘러싼 가장 안쪽 반복문 또는switch를 빠져나갑니다. 다음 레이블로 일부러 이어갈 때는 C++17의[[fallthrough]];로 의도를 표시할 수 있습니다.
여러 레이블이 한 본문을 공유
case 1: 다음에 문장 없이 case 3:을 두면 두 레이블이 같은 문장을 시작점으로 공유합니다. 실행한 문장에서 다음 레이블로 떨어지는 것과 구분합니다.
default도 같은 레이블 규칙
소스 어디에 둘 수 있지만 자동 종료되지 않습니다. 마지막이 아니라면 이후 레이블로 이어질 수 있으므로 종료 정책을 함께 읽어야 합니다.
break만이 유일한 종료 수단은 아닙니다. return이나 예외 등 다른 제어 이전도 흐름을 바꿀 수 있으므로 각 레이블에서 실제 도달 경로를 확인합니다.#include <iostream>
int main() {
int choice;
std::cout << "메뉴를 선택하세요 (1:불고기, 2:비빔밥, 3:냉면, 4:음료): ";
std::cin >> choice;
switch (choice) {
case 1:
std::cout << "불고기를 선택하셨습니다." << std::endl;
std::cout << "가격은 12000원입니다." << std::endl;
break; // switch 문 종료
case 2:
std::cout << "비빔밥을 선택하셨습니다." << std::endl;
std::cout << "가격은 9000원입니다." << std::endl;
break; // switch 문 종료
case 3:
std::cout << "냉면을 선택하셨습니다." << std::endl;
std::cout << "가격은 8000원입니다." << std::endl;
break; // switch 문 종료
case 4:
std::cout << "음료를 선택하셨습니다." << std::endl;
std::cout << "가격은 2000원입니다." << std::endl;
break; // switch 문 종료
default: // 어떤 case에도 해당하지 않을 경우
std::cout << "잘못된 메뉴 번호입니다." << std::endl;
break; // default도 break를 붙이는 것이 일반적
}
std::cout << "주문 처리를 완료했습니다." << std::endl;
return 0;
}break와 Fall-through
switch 문에서 실행 중인 문장 뒤에 break나 다른 제어 이전이 없으면 다음 레이블을 지나 문장이 연속적으로 실행됩니다(Fall-through).
이는 의도일 수도 있고 버그일 수도 있으므로, 각 레이블의 종료 경로를 분명히 표현해야 합니다.
#include <iostream>
int main() {
char grade = 'B';
switch (grade) {
case 'A':
std::cout << "탁월합니다!" << std::endl;
// break; // 여기에 break가 없으면 다음 case로 넘어감
case 'B':
std::cout << "훌륭합니다!" << std::endl;
// break; // 여기에 break가 없으면 다음 case로 넘어감
case 'C':
std::cout << "잘 했습니다." << std::endl;
break; // 여기는 break가 있으므로 switch 문 종료
default:
std::cout << "다시 시도하세요." << std::endl;
break;
}
// 출력:
// 훌륭합니다!
// 잘 했습니다.
return 0;
}위 예시에서 grade가 'B'이므로 훌륭합니다!가 출력된 후, break가 없기 때문에 case 'C'로 Fall-through되어 잘 했습니다.까지 출력됩니다.
이 예시에서는 두 번째 출력까지 이어지는 흐름을 코드에서 의도했는지 반드시 확인해야 합니다.
의도적인 Fall-through:
여러 case가 같은 실행 본문을 공유하도록 연속된 레이블을 둘 수 있습니다.
#include <iostream>
int main() {
int month = 2; // 2월
switch (month) {
case 1: // 1월
case 3: // 3월
case 5: // 5월
case 7: // 7월
case 8: // 8월
case 10: // 10월
case 12: // 12월
std::cout << month << "월은 31일까지 있습니다." << std::endl;
break;
case 4: // 4월
case 6: // 6월
case 9: // 9월
case 11: // 11월
std::cout << month << "월은 30일까지 있습니다." << std::endl;
break;
case 2: // 2월
std::cout << month << "월은 28일 또는 29일까지 있습니다." << std::endl;
break;
default:
std::cout << "잘못된 월입니다." << std::endl;
break;
}
// 출력: 2월은 28일 또는 29일까지 있습니다.
return 0;
}이처럼 문장 없이 연속된 case 레이블을 두면 여러 진입점이 같은 실행 본문을 공유합니다. 실행한 문장에서 다음 레이블로 이어지는 fall-through와 달리, 이 패턴에는 [[fallthrough]];가 필요하지 않습니다.
한 레이블의 문장을 실행한 뒤 다음 레이블로 일부러 이어 갈 때는 C++17부터 [[fallthrough]]; 속성(attribute)을 사용할 수 있습니다. 이 속성은 인수를 받지 않는 null 문장에 붙으며, 같은 switch의 다음 case 또는 default 레이블로 이어지는 의도를 컴파일러와 독자에게 알립니다.
// C++17 이상
case 'A':
std::cout << "매우 잘함!" << std::endl;
[[fallthrough]]; // 컴파일러에게 의도적인 fall-through임을 알림
case 'B':
std::cout << "잘함!" << std::endl;
break;if-else if vs. switch
두 조건문 모두 여러 조건을 처리할 수 있지만, 각각 장단점이 있습니다.
if-else if 체인의 장점
- 유연성: 정수·실수·문자열의 비교 결과와 복잡한 논리 표현식처럼
bool로 판단할 수 있는 조건을 사용할 수 있습니다. - 범위 비교: 특정 범위(
score >= 80 && score < 90)를 비교하는 데 유리합니다. - 논리 연산자 사용:
&&,||,!와 같은 논리 연산자를 사용하여 복잡한 조건을 쉽게 조합할 수 있습니다.
switch 문의 장점
- 명확성: 하나의 변수 값을 기준으로 여러
case를 명확하게 나열할 수 있어 코드의 구조가 한눈에 들어옵니다. - 가독성 (특정 상황): 동일한 변수에 대한 다수의 동등 비교를 처리할 때
if-else if보다 가독성이 좋습니다.
- 범위 비교, 복잡한 논리, 실수·문자열의 비교 결과를 조합한다면
if-else if를 사용하세요. - 하나의 정수형·열거형 값이 여러 개의 특정 상수 중 하나와 일치하는 경우라면
switch문이 구조를 더 분명하게 드러낼 수 있습니다.
두 문법의 상대 성능은 C++ 언어가 보장하지 않습니다. 컴파일러는 어느 쪽이든 최적화할 수 있으므로 먼저 조건의 형태와 가독성에 맞춰 선택하고, 성능이 중요한 경로는 실제 빌드 설정과 입력으로 측정하세요.
switch 문은 값 목록이 분명할 때 읽기 좋지만, break와 default 정책을 빠뜨리면 의도하지 않은 흐름이 생길 수 있습니다.