LEARNING · STAGE 00 · PROGRAMMING BASICS

Инструкции и выражения

В прошлом уроке ты уже написал рабочую программу с std::cout, #include и return 0; — но не называл вещи, из которых она состоит, своими именами. Этот урок вводит два термина, которые дальше будут использоваться в каждой теме курса без дополнительных объяснений: инструкция (statement) и выражение (expression).

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

  • Чем инструкция отличается от выражения
  • Что такое блок кода и зачем нужны фигурные скобки { }
  • Как выражение можно вычислить прямо внутри другой инструкции, без промежуточной переменной
  • Какие ошибки чаще всего возникают из-за путаницы между инструкцией и выражением

02Почему это важно

Дальше в курсе появятся условия (if), циклы (for, while) и функции — и в объяснении каждой из этих тем будут использоваться слова «инструкция», «выражение» и «блок» без повторного определения. Если сейчас не закрепить разницу между ними, придётся либо возвращаться назад за определением, либо додумывать его по контексту — и не всегда правильно. Это короткий урок именно потому, что он вводит словарь, а не новую механику: ты уже писал и инструкции, и выражения в прошлом уроке, просто ещё не называл их так.

03Инструкция и выражение

Код внутри main() состоит из инструкций (statements) — законченных действий. Почти каждая инструкция заканчивается точкой с запятой ;.

main.cppC++
int main()
{
    int x = 10;
    x = x + 5;

    return 0;
}

Внутри инструкций часто встречаются выражения (expressions) — конструкции, которые вычисляются и дают значение (а иногда попутно ещё и производят побочный эффект — например, меняют переменную). В строке x = x + 5; есть оба понятия сразу: x + 5 — это выражение, оно вычисляется в значение 15. Вся же строка x = x + 5; целиком — это инструкция присваивания: она берёт значение выражения x + 5 и сохраняет его в переменную x.

Инструкция и выражение — это не взаимоисключающие категории, а два разных уровня одного и того же кода: выражение вычисляется в значение, инструкция — то, из чего строится последовательность выполнения программы. Сам по себе кусок x + 5 — выражение, а не инструкция: у него нет ;, и он не является самостоятельным шагом программы. Но если приписать к нему ;, получится x + 5; — и это уже отдельная инструкция, построенная из выражения (её называют expression statement, «инструкция-выражение»); именно этот случай разобран в разделе «Типичные ошибки» ниже — компилятор такую строку примет, но вычисленное значение будет никуда не сохранено, потеряно. Другие инструкции устроены иначе и expression statement не являются: int x = 10; — инструкция объявления, return 0; — инструкция возврата, { ... } — составная инструкция (блок, см. ниже). Пока не нужно запоминать все виды — важно не путать «выражение» (вычисляет значение) и «инструкцию» (организует выполнение программы) и знать, что первое становится вторым, если добавить ;.

При этом выражение — не всегда «просто значение без последствий»: x++ тоже выражение, но при вычислении ещё и меняет x; вызов функции вроде foo() — тоже выражение, и он может делать что угодно внутри себя, в том числе выводить текст (как в следующем абзаце). Подробности не нужны прямо сейчас, но полезно сразу не запоминать «выражение ничего не делает» как абсолютное правило.

Из прошлого урока: std::cout << "Hello, C++!\n"; — тоже инструкция (expression statement, построенная из выражения std::cout << "Hello, C++!\n"), а "Hello, C++!\n" внутри неё — строковый литерал, тоже разновидность выражения (у него есть значение — сама строка).

04Блоки кода

Фигурные скобки { } группируют несколько инструкций в один блок (составную инструкцию) — тело функции main() целиком является таким блоком. Всё, что находится внутри { }, выполняется по порядку, сверху вниз, как единое целое.

Блок из нескольких инструкцийC++
{
    int y = 1;
    y = y + 1;
    std::cout << y;
}

Это разделение — инструкция / выражение / блок — будет всплывать в каждом следующем уроке: у if есть блок, который выполняется, если условие истинно; у цикла есть блок, который повторяется; у функции есть блок, который выполняется при её вызове. Поэтому полезно закрепить его сейчас, на самом простом примере, а не разбираться параллельно с новой темой позже.

05Выражение можно вычислить прямо на месте

Необязательно сначала сохранять результат выражения в переменную, а потом отдельно его выводить — выражение можно вычислить прямо внутри вызова std::cout:

Progressive examplesC++
int x = 10;              // инструкция объявления — x существует и равен 10
x = x + 5;                // инструкция присваивания — внутри неё выражение x + 5
std::cout << x;           // выводит уже готовое значение x (15)
std::cout << (x + 100);   // выражение x + 100 вычисляется прямо здесь — выведет 115, x при этом не меняется

В последней строке ничего не сохраняется в переменную — x + 100 вычисляется, полученное значение сразу передаётся в std::cout, а сам x остаётся равным 15. Скобки вокруг x + 100 здесь не обязательны, но делают код нагляднее — явно видно, что именно является одним выражением.

06Инструкция или выражение — таблица

КодЧто этоПояснение
x + 5выражениевычисляется, даёт значение 15; без ; — не является инструкцией, само по себе не сохраняется и не выводится
x + 5;инструкция (expression statement)то же выражение x + 5, но с ; — синтаксически полноценная инструкция; значение вычисляется и тут же отбрасывается (см. «Типичные ошибки»)
x++выражение с побочным эффектомвычисляет значение и изменяет x — пример того, что выражение не всегда «просто значение без последствий»
x = x + 5;инструкция (присваивания)берёт значение выражения x + 5 и сохраняет его в x
int x = 10;инструкция (объявления)создаёт переменную x и сразу инициализирует её значением 10
std::cout << x;инструкция (expression statement)построена из выражения std::cout << x, где x — вложенное выражение, вычисляющееся в своё текущее значение
{ ... }блок (составная инструкция)группирует несколько инструкций в одну единицу

07Хуже → лучше: промежуточная переменная ради одного использования

Задача — вывести x + 100. Три способа решить её, от лишнего кода до идиоматичного.

Хуже — переменная нужна только один разC++
int x = 10;
x = x + 5;
int resultValue = x + 100;
std::cout << resultValue;

Работает правильно, но resultValue объявлена, использована ровно один раз и больше никогда не встречается — лишняя инструкция и лишнее имя, которое нужно придумать и прочитать, не добавляя понимания.

Лучше — выражение вычислено прямо внутри coutC++
int x = 10;
x = x + 5;
std::cout << (x + 100);

Ровно то же поведение, без промежуточного шага — именно этому учит раздел 05 выше и это же проверяет практика ниже (задание 2). Правило не абсолютное: если бы выражение было длинным и малопонятным без контекста, переменная с хорошим именем могла бы, наоборот, сделать код яснее — но x + 100 достаточно короткое и самоочевидное, чтобы инлайн был лучшим выбором.

Лучше всего — оба значения в одном выводеC++
int x = 10;
x = x + 5;
std::cout << "x = " << x << ", x + 100 = " << (x + 100) << "\n";

Здесь применена цепочка << из прошлого урока вместе с инлайн-вычислением из этого — один читаемый вывод вместо двух разрозненных инструкций std::cout. Не всегда нужно доводить код именно до такого вида — но полезно видеть, что оба приёма сочетаются.

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

Прежде чем переходить к практике ниже, поэкспериментируй с примером из раздела 05 прямо в редакторе кода на этой странице:

  • Замени std::cout << (x + 100); на std::cout << (x * 2); — результат станет 30, а не 115.
  • Добавь ещё одну инструкцию x = x + 1; между присваиванием и выводом — итоговое значение x изменится на единицу.
  • Попробуй убрать ; в конце любой инструкции — компилятор откажется собирать программу (см. типичные ошибки ниже).

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

Точный текст ошибки зависит от компилятора и его версии — ниже характерный вид в стиле g++.

Забыт ; в конце инструкции
int x = 10;
x = x + 5   // забыт ; в конце инструкции
return 0;
консоль (иллюстрация)
g++ main.cpp -o main main.cpp: In function 'int main()': main.cpp:5:10: error: expected ';' before 'return' 5 | x = x + 5 | ^ | ; Компиляция не удалась.

Ошибка привязана к строке 5, хотя реальная причина — конец этой же строки: компилятор понимает, что чего-то не хватает, только дойдя до return на следующей строке.

Выражение написано как отдельная инструкция и результат потерян
x + 5;   // вычислено и тут же отброшено — результат нигде не сохранён

Это синтаксически корректная инструкция-выражение (expression statement, см. раздел 03) — компилятор примет её без единой ошибки или предупреждения в консоли, вычислит x + 5 и отбросит результат. Опаснее синтаксической ошибки именно потому, что компилятор молчит, а результат работы программы просто не будет тем, что ожидалось.

Повторное объявление переменной в том же блоке
int x = 10;
int x = 20;   // не присваивание, а повторное объявление — ошибка
консоль (иллюстрация)
g++ main.cpp -o main main.cpp: In function 'int main()': main.cpp:5:9: error: redefinition of 'int x' 5 | int x = 20; | ^ main.cpp:4:9: note: 'int x' previously declared here 4 | int x = 10; | ^ Компиляция не удалась.

Компилятор обычно ещё и указывает («note»), где было первое объявление. Чтобы изменить значение существующей переменной, используется присваивание без int: x = 20;.

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

Три вопроса на разное: классификация («что это»), предсказание результата и типичная ловушка. Сначала попробуй ответить сам — ответ никуда не убежит.

Классификация
Ниже — четыре фрагмента кода. Только ОДИН из вариантов ответа классифицирует ВСЕ четыре правильно (выражение / инструкция / блок). Какой?
1) y + 1
2) y = y + 1;
3) { int y = 1; y = y + 1; }
4) std::cout << y;
Показать объяснение

1) y + 1 — выражение: вычисляется в значение, но без ; не является отдельным шагом программы. 2) y = y + 1; — инструкция присваивания: берёт значение выражения y + 1 и сохраняет в y. 3) { ... } — блок (составная инструкция): группирует две инструкции внутри { }. 4) std::cout << y; — инструкция (expression statement), построенная из выражения std::cout << y. Частые ошибки: считать y + 1 без ; уже инструкцией (вариант a), или наоборот — считать, что раз выражение лежит внутри, то и y = y + 1;/std::cout << y; остаются «просто выражениями» (вариант b).

Предскажи результат
Что выведет эта программа?
int a = 5;
a = a + 2;
std::cout << (a * 2) << "\n";
std::cout << a;
Показать объяснение

Сначала 14, потом 7. После a = a + 2; переменная a равна 7. Выражение a * 2 в первой строке вывода вычисляется «на лету» и никуда не сохраняется — сам a от этого не меняется, поэтому вторая строка выводит по-прежнему 7, а не удвоенное значение.

Найди ошибку
Программист ожидал увидеть 13. Что программа реально выведет?
int b = 3;
b + 10;
std::cout << b;
Показать объяснение

Выведет 3. Строка b + 10; — синтаксически корректная инструкция-выражение (см. §03 и §09): значение 13 вычисляется и тут же отбрасывается, b при этом не меняется — присваивания здесь нет. Чтобы получить 13, нужно либо b = b + 10; (изменить b), либо std::cout << (b + 10); (вывести выражение напрямую).

11Практика

★ Добавь ещё одну инструкцию, изменяющую x (например, x = x * 2;), и выведи итоговое значение x через std::cout.

★★ Выведи через std::cout выражение x + 100, не сохраняя его предварительно в отдельную переменную.

★★★ Напиши короткую программу с нуля: заведи переменную с любым именем и начальным значением, измени её значение минимум двумя разными инструкциями, а затем выведи через std::cout выражение с этой переменной, вычисленное прямо на месте — без сохранения в отдельную переменную. Конкретное имя, тип, числа и порядок действий выбирай сам; используй только то, что разобрано в этом и прошлом уроке.

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

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

  • Ты можешь отличить выражение от инструкции в любом фрагменте кода и объяснить, по какому признаку ты это определил.
  • Ты можешь объяснить своими словами разницу между «вычисляет значение» (выражение) и «выполняет шаг программы» (инструкция) — и то, что первое становится вторым при добавлении ;.
  • Ты можешь найти выражение внутри более крупной конструкции — например, увидеть x + 100 внутри std::cout << (x + 100); или y + 1 внутри y = y + 1;.
  • Ты можешь разобрать простую C++-программу построчно на инструкции, выражения и блоки — не как абстрактное упражнение, а чтобы понимать, что реально происходит при выполнении.
  • Ты можешь вычислить выражение прямо внутри инструкции (например, внутри std::cout), не заводя для этого промежуточную переменную — и понимаешь, когда это уместно, а когда лучше сохранить в переменную с именем.

13Кратко

  • Инструкция (statement) — синтаксическая единица, из которой строится выполнение программы; почти всегда заканчивается ;.
  • Выражение (expression) — код, который вычисляется и даёт значение; может иметь и побочный эффект (например, x++ меняет x).
  • Выражение с ; на конце (x + 5;) само становится инструкцией — expression statement, «инструкцией-выражением». Это не исключение из правила, а один из видов инструкций наравне с инструкцией объявления, присваивания, возврата и т. д.
  • Блок { } группирует несколько инструкций в одну составную инструкцию.
  • Выражение можно вычислить прямо внутри другой инструкции (например, внутри std::cout << ...), без промежуточной переменной.
  • Expression statement без сохранения результата (x + 5; само по себе) синтаксически корректен, но бессмысленен — вычисленное значение нигде не остаётся.

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

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

Разделение на инструкции, выражения и блоки — часть самого языка C++, а не что-то специфичное для консольных программ. Внутри любой Unreal-функции — BeginPlay(), Tick(), обработчика делегата — код устроен ровно так же: тело функции — это блок, состоящий из инструкций, а инструкции содержат выражения. Никаких дополнительных правил Unreal здесь не добавляет.