switch и проваливание
В холле большого здания висит указатель: «Бухгалтерия — 3 этаж, отдел кадров — 5 этаж, столовая — 1 этаж». Вы не обходите все этажи по очереди, а находите в указателе свою строку и сразу едете на лифте на нужный этаж.
Цепочка else if похожа на обход этажей. Когда одну переменную сравнивают с несколькими числами, цепочка else if в каждой ветке заново сравнивает ту же переменную с новым числом. Инструкция switch работает как указатель в холле: вычисляет выражение в скобках один раз и переходит к ветке с таким же значением:
#include <iostream>
int main() {
int day = 2;
switch (day) {
case 1:
std::cout << "Monday\n";
break;
case 2:
std::cout << "Tuesday\n"; // Tuesday
break;
default:
std::cout << "another day\n";
}
}Давайте пройдём по программе:
switch (day)вычисляетdayи получает2.- Выполнение переходит сразу к метке
case 2:и печатаетTuesday. breakзавершаетswitch, и доdefaultдело не доходит.
Метки case и default
Слово case с константой и двоеточием — это метка: место, куда переходит выполнение, если значение выражения равно этой константе. В указателе из холла метка — это строка с номером этажа.
breakзавершаетswitch.- Метка
defaultсрабатывает, когда не подошла ни одна меткаcase. - Если ни одна метка не подошла, а
defaultнет, не выполнится ни одна ветка.
Какие значения подходят
У switch есть ограничения:
- выражение в
switchдолжно иметь целый тип: подходятintиchar, аdouble— нет; - после
caseпишут константу, известную при компиляции: литерал вроде2или'a'либоconstexpr-переменную; - две метки с одинаковым значением не скомпилируются.
switch проверяет только равенство. Условие вроде day > 5 в метку не записать — для диапазонов остаётся цепочка else if.
Проваливание
Здесь аналогия с лифтом заканчивается. Лифт останавливается на вашем этаже, а switch без break едет дальше.
Метка не завершает код, который стоит перед ней. Без break выполнение идёт дальше через все следующие метки, включая default, пока не встретит break или конец switch. Это называют проваливанием.
Пройдите программу по шагам и следите, где выполнение входит в switch и где выходит из него:
1–2#include <iostream>3int main() {4 int level = 2;5 switch (level) {6 case 3:7 std::cout << "owner\n";8 case 2:9 std::cout << "admin\n";10 case 1:11 std::cout << "user\n";12 break;13 default:14 std::cout << "guest\n";15 }16 std::cout << "done\n";17}
Переменная level равна 2
Проваливанием пользуются, чтобы у нескольких меток была одна ветка:
char c = 'e';
switch (c) {
case 'a':
case 'e':
case 'o':
std::cout << "vowel\n"; // vowel
break;
default:
std::cout << "other\n";
}При c == 'e' выполнение переходит к метке case 'e':, проходит пустую метку case 'o':, и программа печатает vowel. Буква, для которой нет метки, попадает в default.
Если в ветке перед следующей меткой есть свой код, намеренное проваливание отмечают строкой [[fallthrough]];. Она не меняет поведение программы, а сообщает компилятору и читателю, что break пропущен не по ошибке:
int level = 2;
switch (level) {
case 2:
std::cout << "admin ";
[[fallthrough]];
case 1:
std::cout << "user\n"; // admin user
break;
}Давайте разберём оба случая:
- При
level == 2программа печатаетadmin, проваливается вcase 1:и допечатываетuser. Получаетсяadmin user. - При
level == 1выполнение начинается сcase 1:, и программа печатает толькоuser.
Частая ошибка
В ветке забыли break:
int day = 1;
switch (day) {
case 1:
std::cout << "Monday\n"; // ✗
case 2:
std::cout << "Tuesday\n";
break;
}warning: unannotated fall-through between switch labels [-Wimplicit-fallthrough]При day == 1 программа печатает и Monday, и Tuesday: выполнение проваливается в case 2. Clang предупреждает об этом только с флагом -Wimplicit-fallthrough, а GCC — уже с флагом -Wextra. Заканчивайте каждую ветку break, а намеренное проваливание отмечайте [[fallthrough]]. Что напечатает switch без break — частый вопрос на собеседовании.
Главное
switchвычисляет выражение один раз и переходит к меткеcaseс таким же значением, а если её нет — кdefault.- Выражение в
switchимеет целый тип, а послеcaseстоит константа, известная при компиляции. - Без
breakвыполнение проваливается через следующие метки доbreakили концаswitch. - Намеренное проваливание отмечают
[[fallthrough]];.
Подпишись, чтобы пройти курс
Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.