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 дорівнює 10\n";
// }
if (x == 10) { // добре: ви порівнюєте x з 10
std::cout << "x дорівнює 10\n";
} else {
std::cout << "x не дорівнює 10\n"; // x не дорівнює 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 оголошено у внутрішньому блоці, тож зовні її вже немає.
Навіщо це потрібно? Область видимості допомагає тримати лад у голові: змінна «живе» саме там, де вона справді потрібна, і не заважає решті коду.
Дуже часто область видимості виникає природно — через 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, «бо так спокійніше». У підсумку половина з них не використовується до середини програми, ви забуваєте, де що зберігається, і починаються магічні числа та запитання «а чому воно змінилося?». Значно простіше оголошувати змінні безпосередньо перед першим використанням — так область видимості буде меншою, а код читатиметься легше.
Помилка № 5: оголошувати змінну всередині блоку й чекати, що вона буде доступна зовні.
Дуже поширена ситуація: усередині if ви порахували discount, а нижче намагаєтеся застосувати його до ціни. Але discount оголошено всередині { ... }, отже, зовні його не існує. Рішення просте: або оголосити змінну зовні й оновлювати її всередині, якщо за змістом вона має жити довше, або перенести використання всередину блоку, якщо воно справді стосується лише цього сценарію.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ