JavaRush /Курси /C++ SELF /Заглиблюємося у змінні

Заглиблюємося у змінні

C++ SELF
Рівень 6 , Лекція 1
Відкрита

1. Пригадуємо змінні

Коли ви пишете програму, вона постійно щось обчислює: суму, остачу, кількість спроб, поточний рахунок, вибране імʼя користувача. Якщо ніде цього не зберігати, програма буде як золота рибка: три секунди — і все, контекст втрачено. Саме для цього й потрібна змінна: вона дає програмі «памʼять» хоча б на час виконання.

Перш ніж рухатися далі, пригадайте, що ви вже знаєте про змінні.

Змінну зручно уявляти як маленький контейнер із наліпкою. Наліпка — це імʼя змінної, контейнер має певний тип, тобто визначає, дані якої «форми» в ньому можна зберігати, а всередині лежить поточне значення. Отже, ця трійка — тип + імʼя + значення — і є робочою моделлю змінної.

Невелика таблиця, щоб закріпити цей образ:

Що це Як це виглядає в коді Що це означає
Тип
int, std::string
«які значення можна зберігати»
Імʼя
score, age, name
«як до неї звертатися в коді»
Значення
10, "Alex"
«що там лежить просто зараз»

Поки що ми не заглиблюємося в типи даних — це тема наступної лекції. Але важливо запамʼятати: тип — це не прикраса. Саме він задає правила гри: що можна робити зі змінною і які значення вона може містити.

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 оголошено всередині { ... }, отже, зовні його не існує. Рішення просте: або оголосити змінну зовні й оновлювати її всередині, якщо за змістом вона має жити довше, або перенести використання всередину блоку, якщо воно справді стосується лише цього сценарію.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ