Урок 3.1

Условные конструкции в C++: if, else if, switch и тернарный оператор

Третий урок по современному C++26: логические выражения и тип bool, операторы if, if-else и else if, switch и тернарный оператор.

Содержание29 разделов

В прошлой статье мы познакомились с типами данных, в том числе с логическим типом bool, научились считывать данные с клавиатуры и выполнять арифметические операции.

Но все наши программы до сих пор работали одинаково: выполнялись строчка за строчкой, сверху вниз, что бы ни ввёл пользователь. Настоящие программы так не работают. Банк начисляет бонус только клиентам с крупным балансом, онлайн-кинотеатр советует фильм в зависимости от возраста, а в соревнованиях золотую медаль получает только тот, кто занял первое место.

Чтобы программа могла «принимать решения», в C++ есть условные конструкции. В этой статье мы разберём их все — от логических выражений до оператора switch и тернарного оператора.

После этой статьи Вы сможете:

  • объяснить, что такое логическое выражение и как с ним работает тип bool;
  • сравнивать значения с помощью операторов ==, !=, <, >, <= и >=;
  • составлять сложные условия с помощью логических операторов &&, || и !;
  • выполнять код только при выполнении условия — с помощью if и if-else;
  • выбирать одну из нескольких веток с помощью каскада else if;
  • использовать оператор switch и понимать, зачем в нём нужен break;
  • коротко записывать простые условия с помощью тернарного оператора ?:.

Что такое логическое выражение

Логическое выражение — это высказывание, на которое можно ответить «да» или «нет». Записать его можно на любом языке: на русском, на языке программирования или даже на языке жестов. Главное, что ответить на него можно только двумя способами.

Посмотрим на несколько примеров:

Четыре примера логических выражений: «Фон презентации — белый» — нет, «Третья планета от Солнца — Земля» — да, 12 % 2 == 1 — нет, (5 > 3) и (7 + 2 <= 10) — да.
Рисунок 1 — Логические выражения и ответы на них
  • «Фон презентации — белый» — ложь: фон на слайде тёмный, поэтому отвечаем «нет»;
  • «Третья планета от Солнца — Земля» — истина, отвечаем «да»;
  • 12 % 2 == 1 — ложь. Вспомним прошлый урок: если 12 айфонов поровну разделить между двумя друзьями, каждый получит по 6, и не останется ни одного. Остаток от деления 12 на 2 равен 0, а не 1;
  • (5 > 3) и (7 + 2 <= 10) — составное выражение. Обе его части истинны, поэтому в целом отвечаем «да».

Логические выражения — основа всех условных конструкций. Для работы с ними в C++ есть логический тип данных bool.

Тип данных bool

Переменная типа bool хранит одно из двух значений: true (истина) или false (ложь). Объявляется она так же, как переменные других типов:

main.cpp
C++
#include <iostream>

int main() {
    bool flag = false;
    bool flag2 = true;
    return 0;
}

В C++ любое ненулевое число неявно приводится к true, а ноль — к false. Поэтому запись bool flag2 = 123; означает то же самое, что bool flag2 = true;. Числа 1, 2 и −123 тоже превратятся в true, и только 0 — в false.

Обратное преобразование тоже работает: при выводе на экран и в арифметике true превращается в 1, а false — в 0. Поэтому логические значения часто записывают и выводят как единицы и нули.

Операторы сравнения

Чаще всего логические выражения строят с помощью операторов сравнения. Они работают так же, как в математике:

ОператорЗначениеПримерРезультат при a = 5, b = 3
>большеa > btrue
>=больше или равноa >= 5true
<меньшеa < bfalse
<=меньше или равноb <= 2false
==равноa == bfalse
!=не равноa != btrue

Напишем программу, которая сравнивает два введённых числа:

main.cpp
C++
#include <iostream>

int main() {
    int a, b;
    std::cin >> a >> b;
    std::cout << (a == b);
    return 0;
}

Если ввести 1 1, программа выведет 1: числа равны. А если ввести 2 3, увидим 0:

terminal
Bash
2 3
0

Результат сравнения — значение типа bool, и по умолчанию std::cout выводит его как 1 или 0.

Как вывести true и false вместо 1 и 0

Если хотите видеть на экране слова true и false, используйте манипулятор std::boolalpha. Его записывают после std::cout, перед выводимым значением:

main.cpp
C++
#include <iostream>

int main() {
    int a, b;
    std::cin >> a >> b;
    std::cout << std::boolalpha << (a == b);
    return 0;
}
terminal
Bash
-123 123
false

Манипулятор меняет поведение потока вывода: после него все логические значения выводятся словами. В видео для этого подключается библиотека <iomanip>, но std::boolalpha доступен и с одной <iostream>. Библиотека <iomanip> понадобится для других манипуляторов, например std::setprecision.

В обычных программах std::boolalpha нужен нечасто, но в задачах к этому уроку он пригодится.

Логические операторы: И, ИЛИ, НЕ

Из простых сравнений можно собирать составные логические выражения. Для этого в C++ есть три логических оператора:

ОператорНазваниеКогда выражение истинно
&&логическое Иистинны обе части
||логическое ИЛИистинна хотя бы одна часть
!логическое НЕ (отрицание)выражение после ! ложно

Выражения, которые соединяет логический оператор, называют операндами — это то, чем оператор «оперирует». В выражении a > b && a % 2 == 0 левый операнд — a > b, а правый — a % 2 == 0.

Логическое И: оператор &&

Выражение с оператором && истинно тогда и только тогда, когда истинны обе его части. Это легко понять на жизненном примере. Фраза «мы живём на планете Земля, и наша звезда — Солнце» — правда, потому что верны обе её части. А фраза «я жираф, и мы живём на Земле» — ложь: вторая часть верна, но я не жираф.

Результат логического оператора для всех возможных значений операндов удобно записать в таблицу истинности. У оператора && истина получается только в первой строке — когда обе части истинны:

ABA && B
truetruetrue
truefalsefalse
falsetruefalse
falsefalsefalse

Проверим, что первое введённое число больше второго и при этом чётное. Число чётное тогда и только тогда, когда остаток от его деления на 2 равен 0:

main.cpp
C++
#include <iostream>

int main() {
    int a, b;
    std::cin >> a >> b;
    std::cout << (a > b && a % 2 == 0);
    return 0;
}
terminal
Bash
10 7
1

10 больше 7, и 10 — чётное число, поэтому выражение истинно. Оператором && можно соединить сколько угодно условий, например: a > b && a % 2 == 0 && b % 2 == 0.

Логическое ИЛИ: оператор ||

Для оператора || достаточно, чтобы истинной была хотя бы одна часть: левая, правая или обе сразу. Фраза «я жираф, или мы живём на Земле» — правда: пусть я и не жираф, зато мы действительно живём на Земле.

Таблица истинности оператора ||. Ложь получается только в последней строке — когда обе части ложны:

ABA || B
truetruetrue
truefalsetrue
falsetruetrue
falsefalsefalse
main.cpp
C++
std::cout << (a > b || a % 2 == 0);
terminal
Bash
2 7
1

2 больше 7 — ложь, но 2 — чётное число, поэтому всё выражение истинно.

Логическое НЕ: оператор !

Оператор ! меняет значение выражения на противоположное: истину превращает в ложь, а ложь — в истину. У него всего один операнд, поэтому и таблица истинности короткая:

A!A
truefalse
falsetrue

Проверим, что неверно утверждение «оба числа чётные»:

main.cpp
C++
std::cout << !(a % 2 == 0 && b % 2 == 0);
terminal
Bash
2 4
0

Оба числа чётные, поэтому выражение в скобках истинно, а отрицание превращает его в ложь — на экране 0. Если ввести 3 4, выражение в скобках будет ложным, потому что 3 — нечётное число, а после отрицания получится 1.

Приоритет логических операторов

Логические операторы выполняются в таком порядке: сначала ! (НЕ), затем && (И) и только потом || (ИЛИ). Операторы сравнения выполняются раньше, чем && и ||, поэтому в выражении a > b && a % 2 == 0 скобки не нужны.

Чтобы изменить порядок вычислений, используйте круглые скобки. В примере с отрицанием скобки заставляют сначала вычислить &&, а уже потом применить ! ко всему результату:

main.cpp
C++
// ! применяется ко всему выражению в скобках
!(a % 2 == 0 && b % 2 == 0)

// ! применяется только к первому сравнению
!(a % 2 == 0) && b % 2 == 0

Есть ещё одна полезная особенность: операторы && и || вычисляют операнды слева направо и останавливаются, как только результат уже известен. Если левая часть && ложна, правая не вычисляется — всё выражение и так будет ложным. Так же и с ||: если левая часть истинна, правую можно не проверять.

С логическими выражениями мы разобрались. Это фундамент, благодаря которому дальше мы сможем изучать if, switch и тернарный оператор.

Линейная программа и блок-схемы

Прежде чем переходить к условному оператору, посмотрим, как устроены программы, которые мы писали до сих пор. Изобразим их в виде блок-схемы — схемы, где каждый шаг программы нарисован отдельным блоком, а стрелки показывают порядок выполнения.

Программа начинается, вводит данные, выполняет одно действие за другим, выводит результат и завершается. Такая программа называется линейной: в ней нет условий, и все действия выполняются всегда, в одном и том же порядке.

А теперь пусть действие 1 выполняется, только если верно некоторое условие. Например, мы владельцы онлайн-банка и проводим акцию: если на счету клиента больше 1000 рублей, дарим ему 10% от суммы. Клиенту с балансом 1100 рублей начислим 110 рублей, а клиенту с балансом 500 рублей — ничего.

Две блок-схемы. Слева линейная программа: ввод данных, действия 1, 2, …, n, вывод данных. Справа программа с оператором if: если условие верно, выполняется действие 1, иначе программа сразу переходит к действию 2.
Рисунок 2 — Блок-схемы линейной программы и программы с оператором if

После ввода данных на схеме появляется условие — ромб. Если ответ «да», выполняется действие 1, а затем действие 2. Если ответ «нет», программа сразу переходит к действию 2.

Условный оператор if

Такую логику в C++ записывают с помощью оператора if. В этом учебнике мы будем называть его неполным условным оператором — скоро станет понятно почему.

main.cpp
C++
if (/* логическое выражение */) {
    // действия, если условие истинно
}

После слова if в круглых скобках обязательно записывают логическое выражение — такое, какие мы строили из операторов сравнения и логических операторов. Затем в фигурных скобках записывают действия, которые нужно выполнить, если на условие мы отвечаем «да». На блок-схеме это действие 1.

Вернёмся к примеру с банком:

main.cpp
C++
#include <iostream>

int main() {
    double balance;
    std::cin >> balance;

    if (balance > 1000.0) {
        balance *= 1.1;
    }

    std::cout << "Ваш баланс с учётом бонусов: ";
    std::cout << balance << "\n";
    return 0;
}
terminal
Bash
1100
Ваш баланс с учётом бонусов: 1210

Вводим баланс клиента. Если он больше 1000, умножаем его на 1,1 — это и есть начисление 10%. Если баланс 1000 или меньше, тело условия пропускается. В конце выводим итоговый баланс.

Полный условный оператор if-else

Иногда нужно не просто пропустить действие, а выполнить другое. Например, мы хотим посоветовать пользователю фильм в зависимости от возраста: взрослым — «Форсаж», детям — мультфильм. Для этого есть полный условный оператор if-else.

Блок-схема оператора if-else: после ввода данных проверяется условие; если да — выполняется действие 1, если нет — действие 1e; обе ветки затем переходят к действию 2. Справа синтаксис if-else в C++.
Рисунок 3 — Блок-схема и синтаксис оператора if-else

Слово else в переводе с английского означает «иначе». Если на условие мы отвечаем «да», выполняется действие 1, а если «нет» — действие 1e. После этого программа в любом случае переходит к действию 2.

main.cpp
C++
if (/* логическое выражение */) {
    // действия, если условие истинно
} else {
    // действия, если условие ложно
}

Синтаксис почти не отличается от обычного if: после закрывающей фигурной скобки добавляется слово else и ещё одна пара фигурных скобок. Для примера проверим, может ли у пользователя быть паспорт:

main.cpp
C++
#include <iostream>

int main() {
    int age;
    std::cin >> age;

    std::cout << "У пользователя ";

    if (age >= 14) {
        std::cout << "есть паспорт\n";
    } else {
        std::cout << "нет паспорта\n";
    }
    return 0;
}
terminal
Bash
16
У пользователя есть паспорт

В России паспорт получают в 14 лет, поэтому для пользователей от 14 лет программа выводит «есть паспорт», а для младших — «нет паспорта».

Каскадный оператор else if

Операторы if и if-else — самая простая часть урока. Теперь разберём каскадный оператор else if. Он нужен, когда вариантов больше двух.

Задача про инопланетян

Представим, что мы работаем программистами в космическом агентстве. Учёные обнаружили инопланетную жизнь и попросили нас написать программу, которая определяет расу существа по количеству глаз:

Таблица классификации инопланетян: 0 глаз — Blind, 1 глаз — Cyclops, 2 глаза — Human, 3 и больше — Multi-eye.
Рисунок 4 — Классификация инопланетян по количеству глаз

Эту задачу можно решить уже знакомым нам оператором if:

main.cpp
C++
if (eyes == 0) {
    std::cout << "Blind";
}

if (eyes == 1) {
    std::cout << "Cyclops";
}

if (eyes == 2) {
    std::cout << "Human";
}

if (eyes >= 3) {
    std::cout << "Multi-eye";
}

Код рабочий, подвохов в нём нет. Но наша задача — писать не просто работающие программы, а быстрые. Если сайт открывается две минуты, Вы, скорее всего, закроете его, не дождавшись. Программы на C++ тоже должны работать как можно быстрее.

Почему несколько if проверяют все условия

Вычисление логического выражения требует процессорного времени. В нашем примере четыре логических выражения. Сравнение с числом выполняется мгновенно, но представим, что вместо него вычисляется что-то сложное, например траектория полёта ракеты. Пусть каждое условие вычисляется 30 секунд.

Сколько будет работать программа, если у инопланетянина 0 глаз? Проверяем первое условие, тратим 30 секунд и выводим Blind. Казалось бы, на этом можно остановиться: если у существа 0 глаз, их точно не два. Но C++ этого не знает. Он проверит второе, третье и четвёртое условия и потратит ещё 90 секунд. Итого 120 секунд, из которых 90 ушли на заведомо лишние проверки.

Причём какое бы количество глаз мы ни ввели, программа всегда проверит все четыре условия и будет работать 120 секунд.

Наши условия взаимоисключающие: если выполнилось одно, остальные выполниться уже не могут. Если у меня два глаза, их не может быть пять. Понять, что условия взаимоисключающие, помогают здравый смысл и условие задачи. Как раз для таких случаев и нужен оператор else if.

Как работает else if

Блок-схема каскада else if: проверяется условие 1, если нет — условие 2, если нет — условие 3. При ответе «да» на любое условие выполняется соответствующее действие, и программа переходит к действиям после if.
Рисунок 5 — Блок-схема каскадного оператора else if

Сначала проверяется условие 1. Если оно истинно, выполняется действие 1, и программа выходит из всей цепочки, не проверяя остальные условия. Если условие 1 ложно, проверяется условие 2. Если ложно и оно — условие 3, и так далее. Как только мы ответили «да», каскадная проверка заканчивается.

Синтаксис читается сверху вниз:

main.cpp
C++
if (/* условие 1 */) {
    // условие 1 истинно
} else if (/* условие 2 */) {
    // условие 2 истинно, условие 1 ложно
} else if (/* условие 3 */) {
    // условие 3 истинно, условия 1 и 2 ложны
} else {
    // во всех остальных случаях
}

Ветка else в конце необязательна. Если она есть, то срабатывает, когда не выполнилось ни одно из условий.

Решение задачи с помощью else if

main.cpp
C++
#include <iostream>

int main() {
    int eyes;
    std::cin >> eyes;

    if (eyes == 0) {
        std::cout << "Blind";
    } else if (eyes == 1) {
        std::cout << "Cyclops";
    } else if (eyes == 2) {
        std::cout << "Human";
    } else if (eyes >= 3) {
        std::cout << "Multi-eye";
    }
    return 0;
}

Посчитаем время работы. Если у инопланетянина 0 глаз, проверяется только первое условие — 30 секунд. Если 1 глаз, проверяются два условия — 60 секунд. Если 2 глаза — 90 секунд, а если 3 и больше — 120 секунд.

Сравнение времени работы: четыре отдельных if всегда проверяют все условия — 120 секунд при любом количестве глаз; каскад else if останавливается на первом истинном условии — 30, 60, 90 или 120 секунд.
Рисунок 6 — Время работы программы с отдельными if и с каскадом else if

Для части входных данных программа стала работать быстрее, и это несомненный плюс.

Оператор switch

Следующий оператор — switch. Его ещё называют оператором выбора: он выбирает, какую ветку кода выполнить, в зависимости от значения переменной.

main.cpp
C++
switch (value) {
    case значение_1: действия_1; break;
    case значение_2: действия_2; break;
    // ...
    default: действия_иначе;
}

После слова switch в круглых скобках указывают значение, которое будем перебирать. Затем идут ветки case — случаи, когда value равно определённому значению. Ветка default выполняется, если не подошёл ни один case.

Разберём switch на примере. Мы участвовали в забеге и хотим вывести цвет медали в зависимости от занятого места: за первое место — золото, за второе — серебро, за третье — бронза, за остальные — «нет медали».

main.cpp
C++
#include <iostream>

int main() {
    int medal;
    std::cin >> medal;

    switch (medal) {
        case 1: std::cout << "GOLD\n"; break;
        case 2: std::cout << "SILVER\n"; break;
        case 3: std::cout << "BRONZE\n"; break;
        default: std::cout << "NO MEDAL\n"; break;
    }
    return 0;
}
terminal
Bash
2
SILVER

Оператор switch сравнивает переменную со значениями case именно на равенство. По сути, он делает то же самое, что такая цепочка:

main.cpp
C++
if (medal == 1) {
    // GOLD
} else if (medal == 2) {
    // SILVER
} else if (medal == 3) {
    // BRONZE
} else {
    // default: NO MEDAL
}

Если ввести 12, ни один case не подойдёт, сработает ветка default, и мы увидим NO MEDAL. Ветка default — это аналог else у оператора if.

Зачем нужен break

Уберём break после первых двух case и посмотрим, что изменится:

main.cpp
C++
switch (medal) {
    case 1: std::cout << "GOLD\n";
    case 2: std::cout << "SILVER\n";
    case 3: std::cout << "BRONZE\n"; break;
    default: std::cout << "NO MEDAL\n";
}
terminal
Bash
1
GOLD
SILVER
BRONZE

Как только switch находит первый подходящий case, он начинает выполнять все команды подряд, сверху вниз, уже не проверяя остальные case. Остановиться он может только в двух случаях: когда дойдёт до конца switch или когда встретит break.

Если ввести 2, первым подходящим окажется case 2: программа выведет SILVER, затем BRONZE и остановится на break.

Если default стоит последним, break после него можно не ставить: switch всё равно закончится. Порядок case тоже не важен: можно написать case 3, case 1, case 2 или, например, case 33, case 12, case 15. Но значения не должны повторяться: два одинаковых case — ошибка компиляции.

Фигурные скобки внутри case

Команды в C++ разделяются точкой с запятой, поэтому код внутри case можно переносить на несколько строк. Тело case можно взять и в фигурные скобки:

main.cpp
C++
switch (medal) {
    case 1: {
        std::cout << "GOLD\n";
        break;
    }
    case 2: std::cout << "SILVER\n"; break;
    // ...
}

Внутри case можно писать сколь угодно сложный код: вложенные if и даже другие switch. Фигурные скобки особенно пригодятся, если внутри case Вы объявляете переменную: без скобок такой код часто не компилируется. Но чаще всего switch используют для простых случаев, как с медалями.

Несколько case подряд и необязательный default

Тело case может быть пустым. Пусть с клавиатуры вводится число, которое гарантированно равно 0, 1, 2 или 3, и нужно вывести, чётное оно или нет:

main.cpp
C++
switch (value) {
    case 0:
    case 2: std::cout << "EVEN\n"; break;
    case 1:
    case 3: std::cout << "ODD\n";
}

Если ввести 0, первым подходящим будет case 0. Его тело пустое, а break нет, поэтому программа «проваливается» дальше, выполняет команды case 2 — выводит EVEN — и останавливается на break. Если ввести 3, программа сразу попадает в case 3 и выводит ODD.

В этом примере нет ветки default: она тоже необязательна.

Какие значения можно использовать в switch

Зафиксируем правила работы switch:

  1. value может быть целым числом (int, short, long long, unsigned и другие целочисленные типы), символом (char), перечислением (enum) или чем-то, что неявно приводится к целочисленному типу;
  2. значения case обязаны быть известны на этапе компиляции: это литералы, const-значения или constexpr-значения;
  3. ветка default необязательна;
  4. break не является обязательным окончанием тела case.

Почему value должно быть целым? switch проверяет его на точное равенство значениям case, а точно сравнивать можно только целые числа и то, что к ним приводится. Символы подходят, потому что хранятся в виде кодов из таблицы ASCII. Вещественные числа сравнивать через == небезопасно: из-за особенностей хранения в памяти два «одинаковых» дробных числа могут оказаться не равны. Перечисления (enum) мы разберём позже — пока достаточно знать, что это удобные имена для последовательности целых чисел 0, 1, 2 и так далее.

Второе правило означает, что после case нельзя ставить переменную, значение которой вводится с клавиатуры, читается из файла или приходит из интернета. Значение должно быть известно ещё до того, как программа скомпилирована. Напомним: компиляция — это перевод исходного кода на C++ в машинный код, который понимает процессор.

main.cpp
C++
const int g = 10;
constexpr int a = 20;

switch (value) {
    case 1:      /* ... */ break;  // целочисленный литерал
    case 'A':    /* ... */ break;  // символьный литерал
    case 2 + 10: /* ... */ break;  // компилятор сам посчитает 12
    case g:      /* ... */ break;  // константа, известная при компиляции
    case a:      /* ... */ break;  // constexpr-значение
}

Литерал — это значение, записанное прямо в коде, например 1 или 'A'. Константа — это переменная, которую нельзя изменить после объявления. Использовать её в case можно, только если её значение известно при компиляции: const int g = 10; подходит, а константа, значение которой прочитано с клавиатуры, — нет, такой код не скомпилируется. Про constexpr мы подробно поговорим в следующих уроках. Пока просто запомните, что такие значения тоже можно использовать в case.

Чем switch отличается от else if

Может показаться, что без switch можно обойтись, и это правда. Например, в Python долгое время не было аналога switch: там пишут if, elif и else, а похожая конструкция match появилась только в версии 3.10. И на C++ можно писать правильные программы, ни разу не использовав switch. Но у этих операторов есть важные различия.

Возможности

В условиях else if можно писать любые логические выражения: x > 1, y == 2 && (x + y) % 2 == 0 и так далее. Оператор switch умеет только одно: проверять value на равенство значениям case. Записать в case условие вроде «value больше 10» или объединить проверки через && нельзя.

main.cpp
C++
if (x > 1) {
    // ...
} else if (y == 2 && (x + y) % 2 == 0) {
    // ...
}

switch (value) {
    case 10: /* ... */ break;
    case -10: /* ... */ break;
    default: /* ... */;
}

Читаемость

Сравните два фрагмента, которые проверяют цвет светофора:

main.cpp
C++
if (value == kYellow) {
    // ...
} else if (value == kGreen) {
    // ...
} else if (value == kRed) {
    // ...
}

switch (value) {
    case kYellow: /* ... */ break;
    case kGreen:  /* ... */ break;
    case kRed:    /* ... */ break;
}

Здесь kYellow, kGreen и kRed — константы, которые обозначают цвета. Какой вариант читается лучше — дело вкуса. Но когда вариантов больше десяти, switch, как правило, читается заметно приятнее.

Скорость выполнения

Самое интересное отличие — скорость. Цепочка else if проверяет условия по очереди, поэтому время её работы растёт линейно с количеством условий. Такую зависимость обозначают O(n), где n — количество условий: 4 условия — 4 проверки, 10 условий — 10 проверок, миллион условий — миллион проверок.

Как быстро растёт время работы программы при увеличении входных данных, показывает асимптотика. switch в зависимости от значений case может работать за O(1), O(log n) или O(n):

  • O(1) — время работы вообще не зависит от количества case: 10 вариантов или миллион — программа сразу переходит к нужной ветке;
  • O(log n) — логарифмическая зависимость. Логарифм отвечает на вопрос, в какую степень нужно возвести основание, чтобы получить число. Например, 2⁵ = 32, поэтому log₂ 32 = 5: из 32 вариантов придётся проверить всего 5. А из 128 вариантов — всего 7: вариантов в четыре раза больше, а проверок лишь на две больше;
  • O(n) — та же линейная зависимость, что и у else if.
Как компилятор реализует switch: таблица переходов за O(1) для плотных значений case, двоичный поиск за O(log n) для разреженных и обычные сравнения за O(n), когда case всего два-три. Цепочка else if проверяет условия по очереди — O(n).
Рисунок 7 — Как компилятор ускоряет switch

Какой способ выбрать, решает компилятор — в зависимости от количества и значений case, а также от уровня оптимизации:

  • если значений много и они идут плотно, подряд — например, 1, 2, 3, …, 10, — компилятор строит таблицу переходов и сразу «прыгает» в нужную ветку за O(1);
  • если значений много, но они разбросаны — например, 5, 100, 500, 800, 1200, — компилятор строит сбалансированное двоичное дерево и находит нужную ветку за O(log n);
  • если case всего два-три, компилятору проще проверить их по очереди, как в else if, — получается O(n).

Пугаться этих терминов не нужно. Главный вывод такой: если Вы проверяете одну переменную на равенство многим значениям, используйте switch. Он будет работать не медленнее цепочки else if, а часто — заметно быстрее. Но применить его можно не всегда, а только для проверок на равенство.

Тернарный оператор

Последний оператор этого урока — тернарный. Он нужен, чтобы короче записывать простые условия if-else. «Тернарный» означает «тройной»: у этого оператора три операнда.

main.cpp
C++
логическое_выражение ? действие_если_да : действие_если_нет

Сначала записывают логическое выражение, затем знак вопроса и то, что нужно сделать, если условие истинно. После двоеточия — то, что нужно сделать, если условие ложно.

main.cpp
C++
2 > 0 ? std::cout << "COOL!" : std::cout << "BAD :(";

2 больше 0, поэтому выполнится часть между знаком вопроса и двоеточием, и на экране появится COOL!. Обратите внимание: внутри тернарного оператора после команд не ставят точку с запятой — она одна, в самом конце.

Этот пример можно записать короче и понятнее:

main.cpp
C++
std::cout << (2 > 0 ? "COOL!" : "BAD :(");

Здесь тернарный оператор выбирает, какую строку подставить в вывод: "COOL!" или "BAD :(". Поэтому его тоже можно назвать оператором выбора. Скобки вокруг тернарного оператора обязательны: как и в случае с ==, оператор << выполняется раньше.

Не усложняйте тернарный оператор

Тернарный оператор можно использовать и внутри выражений:

main.cpp
C++
int a = 5, value = 2;
value += (a % 2 == 0 && value < 0 ? a * 2 : value / 2);

Число 5 нечётное, поэтому условие ложно, и к value прибавится value / 2, то есть 1. В итоге value станет равно 3.

Код рабочий, но читать его тяжело: глаза путаются в знаках вопроса, амперсандах и двоеточиях. Тернарный оператор хорош для коротких условий вроде примера с COOL!. Если условие сложное, используйте обычный if-else.

Подведём итоги

В этой статье Вы познакомились с логическими выражениями и со всеми условными конструкциями C++.

Главное, что нужно запомнить:

  • логическое выражение — это высказывание, на которое можно ответить «да» или «нет». В C++ его значение хранит тип bool: true или false;
  • любое ненулевое число приводится к true, а 0 — к false;
  • для сравнения используйте ==, !=, <, >, <= и >= и не путайте сравнение == с присваиванием =;
  • логические операторы &&, || и ! собирают сложные условия. Сначала выполняется !, потом &&, потом ||;
  • if выполняет код, только если условие истинно, а if-else выбирает одну из двух веток;
  • для взаимоисключающих условий используйте каскад else if: он прекращает проверки после первого истинного условия;
  • switch проверяет целочисленное значение на равенство константам. Не забывайте break в конце case;
  • тернарный оператор условие ? да : нет удобен для коротких условий.

Теория без практики быстро забывается, поэтому обязательно закрепите материал. Авторизуйтесь на сайте, отметьте статью как прочитанную и перейдите во вкладку «Решать задачи». Отправляйте решения на проверку, разбирайте результаты тестов и отслеживайте свой прогресс.

Использованная литература

  1. Рабочий проект стандарта C++ [Электронный ресурс]. — URL: https://eel.is/c++draft/ (дата обращения: 03.10.2026).
  2. Рабочий проект стандарта C++26 — N5054 [Электронный ресурс]. — URL: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2026/n5054.pdf (дата обращения: 03.10.2026).
  3. ISO/IEC 14882:2024 — язык программирования C++ [Электронный ресурс]. — URL: https://www.iso.org/standard/83626.html (дата обращения: 03.10.2026).
  4. Фундаментальные типы данных, тип bool [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/types (дата обращения: 03.10.2026).
  5. Операторы сравнения [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/operator_comparison (дата обращения: 03.10.2026).
  6. Логические операторы [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/operator_logical (дата обращения: 03.10.2026).
  7. Приоритет операторов [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/operator_precedence (дата обращения: 03.10.2026).
  8. Условный оператор if [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/if (дата обращения: 03.10.2026).
  9. Оператор switch [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/switch (дата обращения: 03.10.2026).
  10. Оператор break [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/break (дата обращения: 03.10.2026).
  11. Тернарный условный оператор [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/operator_other (дата обращения: 03.10.2026).
  12. Константные выражения [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/constant_expression (дата обращения: 03.10.2026).
  13. Спецификатор constexpr [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/language/constexpr (дата обращения: 03.10.2026).
  14. Манипулятор std::boolalpha [Электронный ресурс]. — URL: https://en.cppreference.com/w/cpp/io/manip/boolalpha (дата обращения: 03.10.2026).
  15. Programming: Principles and Practice Using C++, 3-е издание [Электронный ресурс]. — URL: https://www.stroustrup.com/PPP3.html (дата обращения: 03.10.2026).
  16. A Tour of C++, 3-е издание [Электронный ресурс]. — URL: https://www.stroustrup.com/tour3.html (дата обращения: 03.10.2026).