1. Вспоминаем переменные
Когда вы пишете программу, она постоянно что-то считает: сумму, остаток, количество попыток, текущий счёт, выбранное имя пользователя. Если не хранить это где-то, программа будет как золотая рыбка: три секунды — и всё, контекст потерян. Переменная как раз и нужна, чтобы у программы появилась «память» хотя бы на время выполнения.
Прежде чем углубиться в этот вопрос, давайте вспомним, что вы уже знаете о переменных.
Переменную удобно представлять как маленький контейнер с наклейкой. Наклейка — это имя переменной, контейнер имеет определённый тип (какой «формы» данные можно хранить), а внутри лежит текущее значение. И вот эта тройка — тип + имя + значение — и есть рабочая модель переменной.
Сразу маленькая таблица, чтобы закрепить образ:
| Что это | Как выглядит в коде | Что означает |
|---|---|---|
| Тип | |
«какие значения можно хранить» |
| Имя | |
«как к этому обращаться в коде» |
| Значение | |
«что там лежит прямо сейчас» |
Мы пока не углубляемся в разные виды типов (это будет следующая лекция уровня). Но важно запомнить: тип — это не украшение. Он задаёт правила игры: что можно делать с переменной и какие значения в неё помещаются.
2. Объявление и инициализация
С переменными у новичков часто путаница из-за того, что в одной и той же строчке мы делаем сразу два действия. Когда вы пишете int x = 10;, вы одновременно «объявляете» переменную (сообщаете компилятору её тип и имя) и «инициализируете» (даёте стартовое значение). Это как завести банковский счёт и сразу положить туда первые деньги — не обязательно, но очень разумно.
В рамках курса у нас будет простое правило безопасности: переменная должна получать начальное значение при объявлении. Это экономит время и спасает от странных багов, когда вы случайно используете значение «из ниоткуда».
Мини‑пример: переменная появляется и сразу получает число.
#include <iostream>
int main() {
int score = 10; // создали переменную score со значением 10
std::cout << score << '\n'; // 10
}
Обратите внимание: слово int — часть объявления. Пока вы не написали тип, переменная ещё не «создана». Имя тоже важно: компилятор должен знать, как к ней обращаться дальше.
Ещё пример: переменная инициализируется выражением. Это всё ещё инициализация — просто справа не «готовое число», а выражение.
#include <iostream>
int main() {
int a = 3;
int b = 4;
int sum = a + b; // sum создаётся и сразу получает 7
std::cout << sum << '\n'; // 7
}
Здесь очень полезно проговаривать себе так: «создаю sum, вычисляю a + b, записываю результат в sum».
3. Присваивание: изменить значение существующей переменной
После того как переменная создана, вы можете обновлять то, что лежит внутри. Это уже другое действие — присваивание. И вот тут появляется ключевая мысль лекции: символ = в C++ означает не “равно”, а “записать новое значение”.
Проще всего запомнить так:
- инициализация — это «создать переменную»;
- присваивание — это «обновить уже существующую».
Пример: сначала создаём переменную, потом изменяем.
#include <iostream>
int main() {
int score = 10; // создать
score = 20; // изменить
std::cout << score << '\n'; // 20
}
А теперь более «живой» пример: меняем значение с использованием старого.
#include <iostream>
int main() {
int score = 10;
score = score + 5; // справа вычисляем 10 + 5, результат 15 записываем в score
std::cout << score << '\n'; // 15
}
Здесь видно важнейшее правило присваивания: правая часть сначала вычисляется, и только потом результат записывается в левую часть.
То есть это НЕ «уравнение» и не «равенство». Это скорее команда: «возьми текущее значение score, прибавь 5, и сохрани обратно в score».
4. Выражения и операторы = и ==
Правая часть — выражение
Когда вы видите строку вида x = ...;, справа почти всегда стоит выражение. Выражение — это кусок кода, который можно вычислить в одно значение. Там может быть просто число, может быть переменная, может быть формула, а может быть даже результат чтения из std::cin (но обычно cin используют отдельной строкой, чтобы было читабельно).
Посмотрим на несколько вариантов и потренируем взгляд.
#include <iostream>
int main() {
int x = 5;
int y = 2;
int a = x; // выражение справа — просто x
int b = x + y; // выражение — сумма
int c = (x + y) * 10; // выражение — более сложная формула
std::cout << a << ' ' << b << ' ' << c << '\n'; // 5 7 70
}
Главная привычка, которую мы вырабатываем: в каждой строке присваивания задавайте себе вопрос: «Что вычисляется справа?» и «Куда это записывается слева?».
Если это довести до автоматизма, дальше вам будет намного проще читать условия, циклы и любые формулы.
= — это не сравнение
Путаница между = и “равно” — настолько классическая, что её можно считать «обрядом посвящения». Почти каждый новичок хотя бы раз пишет в условии if (x = 10), а потом удивляется, что программа ведёт себя… скажем так… творчески.
Важно:
- = — присваивание (изменяет переменную),
- == — сравнение (даёт true или false).
Сравнение вы уже проходили раньше, но сегодня мы добавим акцент: ошибка = вместо == — это ошибка смысла, а не синтаксиса. Иногда компилятор даже не ругнётся так, как вам хотелось бы.
Мини‑пример (покажем как «нельзя», но без углубления):
#include <iostream>
int main() {
int x = 3;
// if (x = 10) { // плохо: вы меняете x на 10
// std::cout << "x is 10\n";
// }
if (x == 10) { // хорошо: вы сравниваете x с 10
std::cout << "x is 10\n";
} else {
std::cout << "x is not 10\n"; // x is not 10
}
}
Мы не будем сейчас подробно разбирать, почему if (x = 10) может компилироваться. Нам важнее практическое правило: если вы в голове произнесли “проверим, равно ли” — в коде почти всегда должен быть ==.
5. Область видимости: где переменная «видна»
Когда программа становится больше, появляется очень практичный вопрос: «А где эта переменная вообще доступна?» В C++ ответ связан с фигурными скобками { ... }. Блок кода задаёт область видимости: имя переменной видно от места объявления до конца текущего блока.
Это похоже на бейджик на конференции: внутри зала все знают, кто вы, а за дверью охрана такая: «Извините, вы кто вообще?». (Компилятор в роли охраны — это отдельный сериал.)
Вот простой пример:
#include <iostream>
int main() {
int x = 1;
{
int y = 2;
std::cout << (x + y) << '\n'; // 3
}
std::cout << x << '\n'; // 1
std::cout << y << '\n'; // ошибка: y здесь не существует
}
Здесь x объявлена во внешнем блоке main, поэтому она видна и снаружи, и внутри внутреннего блока. А y объявлена во внутреннем блоке, и снаружи её уже нет.
Почему это нужно? Потому что scope помогает держать порядок в голове: переменная «живет» там, где она действительно нужна, и не мешает остальному коду.
Очень часто scope появляется естественно через if и циклы. Например, переменная, объявленная внутри if, не обязана существовать после него.
#include <iostream>
int main() {
int temperature = 30;
if (temperature > 25) {
int extraWater = 2;
std::cout << extraWater << '\n'; // 2
}
std::cout << extraWater << '\n'; // ошибка: переменная умерла вместе с блоком
}
И да, формулировка «умерла» тут внезапно очень точная: после закрывающей } имя перестаёт быть доступным, как будто его никогда и не было.
6. Мини‑приложение: «копилка» и обновление баланса
Чтобы переменные не остались абстрактной философией, соберём маленький консольный сценарий, который вы сможете расширять дальше по курсу. Пусть это будет «копилка» (или баланс), куда мы добавляем и откуда вычитаем. Сегодня нас интересует именно разница между созданием значения и изменением.
Начнём с минимального: есть стартовый баланс и пополнение.
#include <iostream>
int main() {
int balance = 0; // стартовое значение
int deposit = 0;
std::cin >> deposit; // вводим пополнение
balance = balance + deposit; // обновляем баланс
std::cout << balance << '\n'; // зависит от ввода
}
Здесь сразу видно два слоя:
Первый слой — «создать переменные»: balance, deposit. Мы их объявили и инициализировали нулями, чтобы они точно имели понятное значение.
Второй слой — «изменить состояние программы»: мы считали deposit и обновили balance. Обновление делается через присваивание, и правая часть — это выражение balance + deposit.
Добавим ещё одно действие: расход. Это всё те же переменные, всё та же механика, просто мы делаем присваивание несколько раз.
#include <iostream>
int main() {
int balance = 0;
int deposit = 0;
std::cin >> deposit;
balance = balance + deposit;
int spend = 0;
std::cin >> spend;
balance = balance - spend;
std::cout << balance << '\n'; // итоговый баланс
}
Если вы внимательно посмотрите, то увидите «шаблон»:
1) создать переменную,
2) получить значение,
3) обновить состояние другой переменной.
Это один из самых типичных паттернов в программировании. И сегодня наша задача — научиться видеть его глазами, а не только руками печатать.
Схема присваивания: порядок действий
Иногда помогает не код, а маленькая картинка. Присваивание можно мысленно разложить на шаги: вычислить правую часть, затем записать в левую часть. Это как заказ в кафе: сначала приготовили блюдо, потом положили на тарелку. Нельзя «положить на тарелку» то, что ещё не приготовлено.
Вот упрощённая блок‑схема:
flowchart TD
A["Строка: x = выражение;"] --> B["Вычислить выражение справа"]
B --> C["Получить результат (одно значение)"]
C --> D["Записать результат в x"]
D --> E["x теперь хранит новое текущее значение"]
Если вы ловите себя на мысли «почему оно так посчитало», очень часто причина в том, что вы неправильно представляете этот порядок. Особенно когда справа тоже встречается x (как в x = x + 5;): сначала вычисление, потом запись.
7. Типичные ошибки
Ошибка №1: использовать переменную до объявления.
Это выглядит невинно: вы пишете std::cout << total << '\n';, а потом ниже добавляете int total = 0;. Для человека это «почти рядом», а для компилятора — строгий порядок: пока переменная не объявлена, её имени не существует. Лечится просто: объявляйте переменную до первого использования, и лучше максимально близко к месту, где она реально нужна.
Ошибка №2: считать, что = — это математическое равенство.
В математике x = x + 1 звучит как бред (и преподаватель матанализа уже тянется к мелу). В программировании это нормальная команда обновления: «возьми старое значение x, прибавь 1, сохрани обратно». Если постоянно помнить “справа вычислили → слева записали”, эта путаница постепенно исчезает.
Ошибка №3: путать = и == в условиях.
Это тот случай, когда программа может начать «работать», но делать не то. Вы хотели сравнить, а вместо этого меняете значение переменной прямо внутри условия. Даже если компилятор выдаст предупреждение, новичок часто его игнорирует, потому что «ну оно же запускается». Дисциплина простая: если вы читаете это как «равно ли», пишите ==.
Ошибка №4: объявлять переменную слишком рано и делать огромную область видимости.
Новички иногда объявляют десяток переменных в начале main, «потому что так спокойнее». В итоге половина из них не используется до середины программы, вы забываете, что где хранится, и начинаются магические числа и “а почему оно поменялось?”. Гораздо проще объявлять переменные прямо перед тем, как они впервые понадобились — так scope получается маленьким, а код читается легче.
Ошибка №5: объявлять переменную внутри блока и ждать, что она будет доступна снаружи.
Очень распространённая ситуация: внутри if вы посчитали discount, а потом ниже пытаетесь применить его к цене. Но discount объявлен внутри { ... }, значит, снаружи он не существует. Решение — объявить переменную снаружи и обновлять внутри (если по смыслу она должна жить дольше), либо перенести использование внутрь блока, если оно действительно относится только к этому сценарию.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ