# Глава 3.2. Конструкция switch-case
Конструкция `switch-case` удобна, когда требуется сравнивать выражение с набором константных значений. Это [более читабельная](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#res-switch-if) замена вложенных `if-else`:
```cpp
switch (выражение)
{
case значение_1:
инструкции_1;
break;
case значение_2:
инструкции_2;
break;
case значение_n:
инструкции_n;
break;
default:
инструкции;
}
```
Например:
```cpp {.example_for_playground .example_for_playground_004}
char op = '+';
double a = 1.0;
double b = 2.0;
switch(op)
{
case '-':
a -= b;
break;
case '+':
a += b;
break;
case '*':
a *= b;
break;
case '/':
a /= b;
break;
default:
std::println("Unsupported operator");
}
std::println("res={}", a);
```
```
res=3
```
Тело `switch` **обязательно** обрамляется фигурными скобками. Вокруг инструкций в блоках `case` и `default` скобки можно не ставить.
Блок `default` опциональный, его можно не ставить. Он срабатывает, если не подошёл ни один из блоков `case`.
Что выведется в консоль? {.task_text}
```cpp {.example_for_playground .example_for_playground_006}
char user_input = 'y';
switch (user_input)
{
case '\n':
std::println("repeat");
break;
case 'y':
std::println("yes");
break;
case 'n':
std::println("no");
break;
default:
std::println("error");
}
```
```consoleoutput {.task_source #cpp_chapter_0032_task_0030}
```
Выполнится блок `case 'y'`. {.task_hint}
```cpp {.task_answer}
yes
```
## Тип выражения для switch
У `switch-case` есть ограничения. Выражение для `switch` должно быть:
- целочисленным (например, `int`),
- символьного типа (например, `char`),
- либо перечислением `enum`, о котором вы скоро [узнаете.](/courses/cpp/chapters/cpp_chapter_0054/)
То есть сравнивать строку `std::string` с использованием `switch` не получится.
## Оператор break
В большинстве случаев после выполнения подходящего `case` требуется выйти из `switch`. Для этого используется оператор `break`:
```cpp {.example_for_playground .example_for_playground_007}
const std::size_t number_system = 10;
switch (number_system)
{
case 2:
std::println("Binary");
break;
case 10:
std::println("Decimal");
break;
case 16:
std::println("Hexadecimal");
break;
default:
std::println("Other");
}
```
```
Decimal
```
Если внутри `case` отсутствует `break`, то код **продолжит выполняться** во всех последующих блоках `case`, даже если их условия не совпадают с результатом выражения! Выполнение продолжится до следующего `break` или до конца `switch`. Такое поведение называют «проваливанием» (fall-through).
Уберём все `break` из примера выше и посмотрим, как изменится консольный вывод:
```cpp {.example_for_playground .example_for_playground_003}
const std::size_t number_system = 10;
switch (number_system)
{
case 2:
std::println("Binary");
case 10:
std::println("Decimal");
case 16: // Выполнится, даже если number_system != 16
std::println("Hexadecimal");
default: // Тоже выполнится
std::println("Other");
}
```
```
Decimal
Hexadecimal
Other
```
Забытый `break` — это _самая распространенная_ ошибка при использовании `switch-case`.
Что выведется в консоль? {.task_text}
```cpp {.example_for_playground .example_for_playground_008}
const std::size_t mark = 3;
switch (mark)
{
case 1:
std::print("e");
case 2:
std::print("d");
case 3:
std::print("c");
case 4:
std::print("b");
case 5:
std::print("a");
}
```
```consoleoutput {.task_source #cpp_chapter_0032_task_0040}
```
Условие попадает под `case 3`, поэтому выполнится его блок. Так как в блоке отсутствует `break`, выполнятся следующие блоки `case`. {.task_hint}
```cpp {.task_answer}
cba
```
Бывают и случаи, когда `break` _специально_ не ставится. Если несколько разных значений нужно обработать одинаково, то соответствующие им блоки `case` идут рядом. Первые остаются пустыми, а в последний размещается необходимая обработка.
Что выведется в консоль? {.task_text}
```cpp {.example_for_playground .example_for_playground_009}
const std::size_t mark = 1;
switch (mark)
{
case 1:
case 2:
case 3:
std::println("bad");
break;
case 4:
std::print("good");
break;
case 5:
std::print("excellent");
break;
default:
std::print("-");
}
```
```consoleoutput {.task_source #cpp_chapter_0032_task_0050}
```
Условие попадает под `case 1`. Это пустой блок, в котором нет `break`. Поэтому выполнится следующий блок `case `, а затем и `case 3`. В `case 3` есть `break`, прерывающий `switch`. {.task_hint}
```cpp {.task_answer}
bad
```
Перепишите эту функцию с применением `switch-case`. {.task_text}
```cpp {.task_source #cpp_chapter_0032_task_0060}
void log_state(int code)
{
std::string state = "";
if (code == 0)
{
state = "Operation succeded";
} else if (code == 1)
{
state = "Still in progress";
} else if (code == 2)
{
state = "Aborted";
} else {
state = "Invalid state";
}
std::println("State of user's operation: {}", state);
}
```
Не забудьте про `break` и `default`. {.task_hint}
```cpp {.task_answer}
void log_state(int code)
{
std::string state = "";
switch (code)
{
case 0:
state = "Operation succeded";
break;
case 1:
state = "Still in progress";
break;
case 2:
state = "Aborted";
break;
default:
state = "Invalid state";
}
std::println("State of user's operation: {}", state);
}
```
----------
## Резюме
- Cопоставление выражения с набором значений через `switch-case` — это удобная замена вложенным `if-else`.
- Выражение для `switch` должно быть либо целочисленным, либо символьного типа, либо перечислением `enum`.
- Внутри блоков `switch-case` не забывайте ставить оператор `break`.
- В `switch-case` есть опциональный блок `default`. Он срабатывает, если не подошёл ни один из блоков `case`.
Наша группа в telegram. Здесь можно задавать вопросы и общаться.
Задонатить. Если вам нравится курс, вы можете поддержать развитие площадки!