LEARNING · STAGE 00 · PROGRAMMING BASICS

Условия: if, else, switch

В прошлом уроке появился bool — результат сравнения. Но сам по себе bool ничего не делает, кроме как хранит true/false. Условные инструкции — это то, что использует этот результат, чтобы решить, какой код выполнять: if, else и switch.

01Что ты изучишь

  • Как if использует bool, чтобы выполнить блок кода условно
  • Цепочки else if / else для нескольких вариантов
  • switch — альтернатива для сравнения одного значения с несколькими вариантами
  • Ловушку забытого break в switch
  • Когда использовать if, а когда switch

02Основная идея: развилка на дороге

Всё, что делают if, else и switch, — это развилка: программа доходит до определённой точки и должна выбрать, по какому из путей пойти дальше, в зависимости от какого-то условия. До сих пор код в этом курсе выполнялся строго сверху вниз, инструкция за инструкцией, без вариантов — как прямая дорога без поворотов. Условные инструкции — первый способ свернуть с этой прямой линии: одна и та же программа при разных значениях переменных пойдёт по разным путям и выполнит разный код. if/else — это развилка на два (или больше) направления по любому условию; switch — тот же принцип, но для частного случая, когда развилка строится вокруг одного конкретного значения.

03if — условное выполнение

Самый простой вариант развилки — «выполнить или не выполнить», без альтернативы. Именно это делает if: берёт выражение типа bool в круглых скобках, и если оно true — выполняет следующий за ним блок { }. Если false — блок целиком пропускается, будто его вообще не было, и программа продолжает со следующей инструкции после него.

main.cppC++
int hp = 0;

if (hp == 0)
{
    std::cout << "Game over\n";
}

hp == 0 — то самое выражение сравнения из прошлого урока, оно и есть условие. Блок { } после if — это ровно та же составная инструкция, что и тело main() (третий урок) — просто теперь она выполняется не всегда, а по условию.

04else и else if

else добавляет альтернативную ветку — код, который выполняется, когда условие if оказалось false. Проще всего увидеть это по шагам — от простого if/else с двумя ветками до полной цепочки на несколько условий.

Шаг 1 — просто else, два вариантаC++
if (hp <= 0)
{
    std::cout << "Game over\n";
}
else
{
    std::cout << "You're fine\n";
}

Ровно два исхода: либо условие hp <= 0 истинно и выполняется первая ветка, либо оно ложно и выполняется else. Третьего не дано — else здесь ловит буквально «всё остальное».

Шаг 2 — добавляем ещё один уровень через else ifC++
if (hp <= 0)
{
    std::cout << "Game over\n";
}
else if (hp < 30)
{
    std::cout << "Low health!\n";
}
else
{
    std::cout << "You're fine\n";
}

else if — это буквально else, внутри которого сразу стоит новый if: «если не первое условие, то проверь ещё вот это». Условия проверяются по порядку сверху вниз, и выполняется ровно одна ветка — та, для которой первое встреченное условие оказалось true (или последняя else, если ни одно условие не подошло). Как только одна ветка выполнена, остальные проверки в этой цепочке пропускаются — даже если следующее условие тоже было бы true. Цепочку можно продолжать сколько угодно раз — else if можно повторять, финальный else не обязателен, но без него какие-то значения могут остаться вообще без обработки.

05switch — сравнение одного значения с вариантами

«Ранг 1 — золото, ранг 2 — серебро, ранг 3 — бронза», записанное через else if, выглядит так: if (rank == 1) ... else if (rank == 2) ... else if (rank == 3) ... — слово rank повторяется три раза подряд, хотя сравнивается всегда одно и то же. Для этого частного случая (одно значение, несколько конкретных вариантов) в C++ есть более компактная запись — switch. Тоже по шагам:

Шаг 1 — минимальный switch, один case и defaultC++
int rank = 2;

switch (rank)
{
    case 1:
        std::cout << "Gold\n";
        break;
    default:
        std::cout << "No medal\n";
        break;
}

switch (rank) — какое значение сравниваем; case 1: — с чем сравниваем; default: — что делать, если ни один case не совпал (аналог финального else). Сейчас rank == 2, ни один case не подходит, поэтому сработает default и выведется «No medal».

Шаг 2 — добавляем ещё вариантыC++
int rank = 2;

switch (rank)
{
    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;
}

Теперь rank == 2 совпадает с case 2:, выполняется std::cout << "Silver\n";, затем break; останавливает выполнение switch целиком — оставшиеся case 3 и default даже не проверяются. break; в конце каждой ветки — не декоративный элемент, а обязательная часть.

Шаг 3 — что будет, если убрать break из case 1C++
int rank = 1;

switch (rank)
{
    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;
}
// вывод при rank == 1:
// Gold
// Silver     ← неожиданно, и это не опечатка в примере

Без break; после case 1: выполнение не останавливается на своей ветке — оно проваливается в следующую (case 2:) целиком, даже несмотря на то, что rank не равен 2. Это называется fallthrough, и это разобрано отдельно в разделе «Типичные ошибки» ниже — по умолчанию это почти всегда баг, а не задумка.

06Вложенные условия: if и switch внутри друг друга

Программа-персонаж продолжает расти: рядом с уже знакомым hp появляется difficulty — уровень сложности, влияющий на то, что выводится игроку. Тело if/else — это блок { }, а внутри блока может быть любая инструкция, включая ещё один if или целый switch. Ничего особенного здесь нет — это тот же принцип вложенности, что и с обычными { } из раздела «else и else if», просто теперь внутри не только std::cout, а целая условная конструкция.

switch внутри else — проверяем сложность, только если персонаж живC++
int hp = 60;
int difficulty = 2;

if (hp <= 0)
{
    std::cout << "Game over\n";
}
else
{
    switch (difficulty)
    {
        case 1:
            std::cout << "Easy mode - alive\n";
            break;
        case 2:
            std::cout << "Normal mode - alive\n";
            break;
        default:
            std::cout << "Hard mode - alive\n";
            break;
    }
}

Внешний if отвечает на один вопрос («жив ли персонаж»), а switch внутри его else-ветки — на другой («какая сложность»), но только для случая, когда персонаж действительно жив. Проверять difficulty, когда hp <= 0, не нужно вовсе — вложенность позволяет не тратить на это отдельную проверку.

Тот же приём работает и в обратную сторону — if внутри if: например, сначала проверить hp <= 0, а внутри этой ветки — ещё отдельный if (hasRevivePotion), чтобы решить, продолжится игра или будет полный game over. Правило то же: чем больше уровней вложенности, тем сложнее читать код, поэтому на практике редко имеет смысл вкладывать глубже двух-трёх уровней — если условий становится больше, обычно лучше вынести часть логики в отдельную функцию (об этом — в одном из следующих уроков).

07if или switch — таблица

if / else ifswitch
Условиелюбое выражение bool (диапазоны, комбинации через &&/||)сравнение одного значения с конкретными константами
Когда удобнеедиапазоны (hp < 30), сложные условиямного конкретных вариантов одного значения (ранг, тип предмета, код команды)
Забыть нельзяфигурные скобки при нескольких инструкциях в веткеbreak; в конце каждого case
Catch-all веткаelse — необязательна, но без неё часть значений может остаться без обработкиdefault: — то же самое, необязательна по синтаксису
Вложенностьлюбая инструкция, включая ещё один if или switch, может быть телом веткито же самое — внутри case может быть целый if/else
Пример для rankif (rank == 1) ... else if (rank == 2) ... else if (rank == 3) ...switch (rank) { case 1: ... case 2: ... case 3: ... }

На одном и том же примере с rank видно, откуда берётся выигрыш switch: слово rank в варианте с if повторяется три раза (по разу на каждое сравнение), а в switch — один раз, в самом начале.

08Хуже → лучше: пропущенные фигурные скобки

Задача — при hp <= 0 вывести две строки: «Game over» и «Restarting...». Отступы в коде выглядят одинаково во всех трёх версиях — но не отступы решают, что относится к if.

Хуже — без { }, вторая строка условной не являетсяC++
int hp = 50;

if (hp <= 0)
    std::cout << "Game over\n";
    std::cout << "Restarting...\n";

При hp = 50 условие hp <= 0 ложно — казалось бы, обе строки должны быть пропущены:

консоль (иллюстрация)
./main Restarting... Ожидалось: ничего. Получено: "Restarting..." при живом hp.

if без { } относится только к одной следующей инструкции — второй std::cout выполняется всегда, независимо от условия, хотя отступ визуально предполагает обратное.

Лучше — с фигурными скобкамиC++
int hp = 50;

if (hp <= 0)
{
    std::cout << "Game over\n";
    std::cout << "Restarting...\n";
}

Теперь обе строки — часть блока, и при hp = 50 не выполнится ни одна из них: вывода нет вообще, как и задумано.

Лучше всего — оба случая явные, ничего не остаётся «по умолчанию»C++
int hp = 50;

if (hp <= 0)
{
    std::cout << "Game over\n";
    std::cout << "Restarting...\n";
}
else
{
    std::cout << "Still alive\n";
}

Добавленный else не обязателен для исправления бага — его исправили скобки. Но else делает код надёжнее иначе: теперь оба исхода (hp кончился / hp ещё есть) написаны явно, и при чтении кода не возникает вопроса «а что происходит, если условие ложно?» — ответ виден сразу.

09Попробуй изменить

  • В примере из раздела «else и else if» поменяй hp на 50 — сработает ветка else («You're fine»).
  • В примере из раздела «switch» убери break; после case 1: — при rank = 1 программа выведет и «Gold», и «Silver» подряд (fallthrough).
  • Добавь в switch ещё один case 4: с новым текстом — синтаксис такой же, как у остальных.

10Живая отладка: типичные ошибки

Как и в прошлом уроке, ни одна из этих ошибок не остановит компиляцию — все три проявляются только во время выполнения.

Забыт break в switch (fallthrough)
int rank = 1;

switch (rank)
{
    case 1:
        std::cout << "Gold\n";
        // break; ← забыли
    case 2:
        std::cout << "Silver\n";
        break;
}
консоль (иллюстрация)
./main Gold Silver Ожидалось только "Gold" — rank равен 1, не 2.

Иногда fallthrough используется намеренно (несколько case подряд для одного и того же кода), но по умолчанию это почти всегда баг. Многие компиляторы предупреждают об этом отдельным warning (-Wimplicit-fallthrough в g++/Clang).

Без фигурных скобок — только следующая инструкция условна

Полный разбор с примером вывода — в разделе «Хуже → лучше» выше. Правило этого курса: фигурные скобки у if/else — всегда, даже для одной инструкции внутри.

= вместо == внутри if
if (hp = 0)   // присваивание вместо сравнения — та же ошибка, что в операторах

Здесь она особенно коварна: результат присваивания (0) приводится к bool как false, поэтому ветка if никогда не выполнится, а заодно hp будет молча обнулён. Компилятор обычно предупреждает об этом (warning), но не всегда останавливает компиляцию.

11Проверь себя

Три вопроса: предсказание выполнения по цепочке условий, диагностика fallthrough и выбор между if и switch.

Предскажи результат
Что выведет эта программа при hp = 25?
if (hp <= 0) { std::cout << "Game over"; }
else if (hp < 30) { std::cout << "Low health!"; }
else { std::cout << "You're fine"; }
Показать объяснение

Выведет «Low health!». Условия проверяются по порядку: hp <= 0 ложно (25 не ≤ 0), поэтому проверяется hp < 30 — истинно (25 < 30), эта ветка выполняется, а else пропускается целиком, даже не проверяясь.

Найди ошибку
При rank = 1 эта программа выводит и «Gold», и «Silver». Как это исправить?
switch (rank) {
  case 1: std::cout << "Gold\n";
  case 2: std::cout << "Silver\n"; break;
}
Показать объяснение

После case 1: нет break; — выполнение не останавливается на своей ветке, а проваливается в следующую (case 2:) целиком, даже хотя rank не равен 2. Это fallthrough (§05). Исправление — добавить break; сразу после std::cout << "Gold\n";.

if или switch?
Что лучше подойдёт для вывода названия дня недели по числу от 1 до 7?
Показать объяснение

switch — одно значение (номер дня) сравнивается с конкретными константами 1..7, ровно тот случай, для которого switch и создан (§05, §07). Для сравнения: задачу «дёшево/средне/дорого» по диапазонам цены (< 10, < 100) switch решить не может вообще — там нужен if/else if, потому что сравниваются диапазоны, а не конкретные значения.

12Практика

★★ По значению hp выведи «Game over» (hp == 0 или меньше), «Low health!» (hp меньше 30) или «You're fine» — с помощью if / else if / else.

★★ По значению rank (1, 2 или 3) выведи «Gold», «Silver» или «Bronze» через switch, с break в каждой ветке и default на прочие случаи.

★★★ По значениям hp и difficulty выведи результат с помощью вложенных условий, как в разделе «Вложенные условия» выше: если hp <= 0 — выведи «Game over» и дальше ничего не проверяй. Иначе — используя switch (difficulty) внутри ветки else, выведи «Easy mode - alive» (1), «Normal mode - alive» (2) или «Hard mode - alive» (для остальных, через default).

13Термины

ТерминЗначит
Ветвление (branching)выполнение разного кода в зависимости от условия — общее название для того, что делают if/else/switch
Условие (condition)выражение типа bool, определяющее, какая ветка выполнится
Fallthroughпровал выполнения из одной ветки case в следующую из-за отсутствующего break;
Вложенность (nesting)размещение одной условной конструкции внутри тела другой

14Что ты теперь умеешь

До этого урока код выполнялся всегда одинаково, сверху вниз. Теперь программа может выбирать путь:

  • Ты можешь написать цепочку if/else if/else для нескольких взаимоисключающих вариантов и предсказать, какая именно ветка выполнится при конкретном значении.
  • Ты можешь выбрать между if и switch для конкретной задачи — и объяснить, почему switch не годится для диапазонов.
  • Ты можешь найти в чужом switch пропущенный break; и объяснить, к какому конкретно выводу приведёт fallthrough.
  • Ты можешь вложить одну условную конструкцию в другую (switch внутри else, if внутри if) и объяснить, зачем это позволяет не дублировать проверку.
  • Ты можешь объяснить, почему пропущенные фигурные скобки у if — не стилистическая мелочь, а источник настоящего бага.

15Кратко

  • if (условие) { ... } — выполняет блок, только если условие true.
  • else if / else — цепочка альтернатив, выполняется ровно одна ветка.
  • switch — компактная альтернатива для сравнения одного значения с несколькими константами; каждая ветка case обязательно заканчивается break;, иначе fallthrough в следующую.
  • Фигурные скобки у if/else — всегда, даже для одной инструкции.
  • = вместо == внутри условия — не ошибка компиляции, а логическая ошибка (обычно с warning).

Условия выбирают путь один раз за проход программы. Следующий урок — циклы: тот же выбор «выполнять или нет», но повторяющийся снова и снова, пока условие остаётся истинным.

🎮 А как это связано с Unreal Engine?

if/else/switch работают в Unreal C++ без изменений — это часть языка, а не движка. В Blueprint (визуальном скриптинге Unreal) им соответствуют ноды Branch и Switch — та же логика, только в виде графа вместо текста:

C++Blueprint-нодаКак выглядит
if (условие) { ... } else { ... }Branchодин вход с bool-условием, два выхода-пина: True и False — выполнение продолжается по одному из них, как две ветки if/else
switch (значение) { case ... }Switch on Int / Switch on Enumодин вход со значением, отдельный выход-пин на каждый вариант плюс пин Default — прямой аналог case и default:

Вложенность работает точно так же: выход True одной Branch-ноды может вести во вход следующей Branch или Switch — визуальный аналог switch внутри else из раздела «Вложенные условия» выше. Когда дойдёт до Blueprint-интеграции курса, эта параллель уже будет знакомой.