Перейти к содержимому
Теория · Условия · урок 3 из 6

switch и проваливание

В холле большого здания висит указатель: «Бухгалтерия — 3 этаж, отдел кадров — 5 этаж, столовая — 1 этаж». Вы не обходите все этажи по очереди, а находите в указателе свою строку и сразу едете на лифте на нужный этаж.

Цепочка else if похожа на обход этажей. Когда одну переменную сравнивают с несколькими числами, цепочка else if в каждой ветке заново сравнивает ту же переменную с новым числом. Инструкция switch работает как указатель в холле: вычисляет выражение в скобках один раз и переходит к ветке с таким же значением:

C++
#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";
    }
}

Давайте пройдём по программе:

  1. switch (day) вычисляет day и получает 2.
  2. Выполнение переходит сразу к метке case 2: и печатает Tuesday.
  3. 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 и где выходит из него:

ВживуюПроваливание без breakшаг 1 из 8 · рамок: 1

Переменная level равна 2

Подсвечена строка, которая выполняется на этом шаге

Проваливанием пользуются, чтобы у нескольких меток была одна ветка:

C++
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 пропущен не по ошибке:

C++
int level = 2;
switch (level) {
    case 2:
        std::cout << "admin ";
        [[fallthrough]];
    case 1:
        std::cout << "user\n";  // admin user
        break;
}

Давайте разберём оба случая:

  1. При level == 2 программа печатает admin , проваливается в case 1: и допечатывает user. Получается admin user.
  2. При level == 1 выполнение начинается с case 1:, и программа печатает только user.

Частая ошибка

В ветке забыли break:

C++
int day = 1;
switch (day) {
    case 1:
        std::cout << "Monday\n";  // ✗
    case 2:
        std::cout << "Tuesday\n";
        break;
}
TEXT
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 и подпишись на канал — откроются все уроки.

Войти другим способом