Цей урок є більш детальним продовженням уроку №13.
Адреси і змінні
Як ви вже знаєте, змінні — це імена шматочків пам’яті, які можуть зберігати інформацію. Пам’ятаємо, що комп’ютери мають оперативну пам’ять, яка доступна програмам для використання. Коли ми визначаємо змінну, частина цієї пам’яті відводиться їй.
Найменша одиниця пам’яті — біт (англ. “bit” від “binary digit“), який може містити значення 0
або 1
. Ви можете думати про біт, як про перемикач світла — або світло вимкнене (0
), або ввімкнене (1
). Чогось середнього між ними немає. Якщо переглянути випадковий шматочок пам’яті, то все, що ви побачите, буде ...011010100101010...
або щось в цьому роді. Пам’ять організована в послідовні частини — адреси. Подібно до того, як ми використовуємо адреси в реальному житті, щоб знайти певний будинок на вулиці, так і тут: адреси дозволяють знайти і отримати доступ до вмісту, який знаходиться в певному місці в пам’яті. Можливо, це здивує вас, але в сучасних комп’ютерах, у кожного біта окремо немає своєї власної адреси. Найменшою одиницею, яка має адрес, є байт (англ. “byte“, який складається з 8 бітів).
Оскільки всі дані комп’ютера — це лише послідовність бітів, то ми використовуємо тип даних (або просто “тип“), щоб повідомити компілятору, як інтерпретувати вміст пам’яті. Ви вже бачили приклад типу даних: int
(цілочисельний тип даних). Коли ми оголошуємо цілочисельну змінну, то ми повідомляємо компілятору, що “шматочок пам’яті, який знаходиться за такою-то адресою, слід інтерпретувати як ціле число”.
Коли ви вказуєте тип даних для змінної, то компілятор і процесор піклуються про деталі перетворення вашого значення в відповідну послідовність бітів певного типу даних. Коли ви просите ваше значення назад, воно “відновлюється” з цієї ж послідовності бітів.
Крім int, є багато інших типів даних в C++, більшість з яких ми детально розглянемо в відповідних уроках.
Фундаментальні типи даних в С++
У C++ є вбудована підтримка певних типів даних. Їх називають основними типами даних (або ще “фундаментальні/базові/вбудовані типи даних“).
Ось список основних типів даних в C++:
Категорія | Тип | Значення | Приклад |
Логічний тип даних | bool | true або false | true |
Символьний тип даних | char, wchar_t, char16_t, char32_t | Один з ASCII-символів | ‘c’ |
Тип даних з плаваючою крапкою | float, double, long double | Десятковий дріб | 3.14159 |
Цілочисельний тип даних | short, int, long, long long | Ціле число | 64 |
Пустота | void | Пустота |
Оголошення змінних
Ви вже знаєте, як оголошувати цілочисельні змінні:
1 |
int nVarName; // int - це тип, а nVarName - це ім'я змінної |
Принцип оголошення змінних інших типів аналогічний:
1 |
type varName; // type - це тип (наприклад, int), а varName - це ім'я змінної |
Оголошення 5 змінних різних типів:
1 2 3 4 5 |
bool bValue; char chValue; int nValue; float fValue; double dValue; |
Зверніть увагу, що змінної типу void тут немає (про це ми поговоримо в наступному уроці).
1 |
void vValue; // не буде працювати, так як void не може використовуватися в якості типу змінної |
Ініціалізація змінних
При оголошенні змінної ми можемо присвоїти їй значення в цей же момент. Це називається ініціалізацією змінної.
C++ підтримує 2 основних способи ініціалізації змінних:
Копіююча ініціалізація (або ще “ініціалізація копіюванням”) з допомогою знаку рівності (=
):
1 |
int nValue = 5; // копіююча ініціалізація |
Пряма ініціалізація з допомогою круглих дужок:
1 |
int nValue(5); // пряма ініціалізація |
Пряма ініціалізація може працювати краще з деякими типами даних, копіююча ініціалізація — з іншими типами даних.
uniform-ініціалізація
Пряма або копіююча ініціалізації працюють не з усіма типами даних (наприклад, ви не зможете використовувати ці форми для ініціалізації списку значень).
У спробі забезпечити єдиний механізм ініціалізації, який буде працювати з усіма типами даних, в C++11 додали нову форму ініціалізації, яка називається uniform-ініціалізацією:
1 |
int value{5}; |
Ініціалізація змінної з порожніми дужками вказує на ініціалізацію за замовчуванням (змінній присвоюється 0
):
1 |
int value{}; // ініціалізація змінної за замовчуванням - значенням 0 |
У uniform-ініціалізації є ще одна додаткова перевага: ви не можете присвоювати змінній значення, яке не підтримує її тип даних — компілятор видасть попередження або повідомлення про помилку, наприклад:
1 |
int value{4.5}; // помилка: цілочисельна змінна не може містити не цілочисельні значення |
Правило: Використовуйте uniform-ініціалізацію.
Присвоювання значень
Коли змінній присвоюється значення після її оголошення (НЕ в момент оголошення), то це копіююче присвоювання (або просто “присвоювання“):
1 2 |
int nValue; nValue = 5; // копіююче присвоювання |
У C++ немає вбудованої підтримки форм прямого/uniform-присвоювання, є тільки копіююче присвоювання.
Оголошення декількох змінних
В одному стейтменті можна оголосити відразу декілька змінних одного і того ж типу даних, розділяючи їх імена комами. Наприклад, наступні 2 фрагменти коду виконують одне і те ж:
1 |
int a, b; |
та:
1 2 |
int a; int b; |
Крім того, ви можете, навіть, ініціалізувати декілька змінних в одному рядку:
1 2 3 |
int a = 5, b = 6; int c(7), d(8); int e{9}, f{10}; |
Є три помилки, які роблять початківці при оголошенні декількох змінних в одному стейтменті:
Помилка №1: Вказувати кожній змінній її (один і той же) тип даних. Це не настільки жахлива помилка, так як компілятор легко її виявить і повідомить вам про це:
1 2 3 |
int a, int b; // неправильно (помилка компіляції) int a, b; // правильно |
Помилка №2: Використання різних типів даних в одному стейтменті. Змінні різних типів повинні бути оголошені в різних стейтментах. Цю помилку компілятор також легко знайде:
1 2 3 4 5 6 7 |
int a, double b; // неправильно (помилка компіляції) int a; double b; // правильно (але не рекомендується) // правильно і рекомендується (+ читабельніше) int a; double b; |
Помилка №3: Ініціалізація двох змінних однією операцією:
1 2 3 |
int a, b = 5; // неправильно (змінна a є неініціалізованою) int a = 5, b = 5; // правильно |
Хороший спосіб запам’ятати цю помилку — використовувати пряму або uniform-ініціалізацію:
1 2 |
int a, b(5); int c, d{5}; |
Так зрозуміліше, що значення 5
присвоюється тільки змінним b
і d
.
Так як ініціалізація декількох змінних в одному рядку є відмінним приводом для здійснення помилок, то ми рекомендуємо визначати декілька змінних в одному рядку тільки в тому випадку, якщо ви будете кожну з них ініціалізувати.
Правило: Уникайте оголошення декількох змінних в одному рядку, якщо не збираєтеся ініціалізувати кожну з них.
Де оголошувати змінні?
Більш старі версії компіляторів мови Сі змушували користувачів оголошувати всі змінні у верхній частині функції:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <iostream> int main() { // Всі змінні в самому верху функції int x; int y; // А потім уже весь інший код std::cout << "Enter a number: "; std::cin >> x; std::cout << "Enter another number: "; std::cin >> y; std::cout << "The sum is: " << x + y << std::endl; return 0; } |
Зараз це вже не обов’язково. Сучасні компілятори не вимагають, щоб всі змінні обов’язково були оголошені в самому верху функції. В С++ пріоритетним є оголошення змінних якомога ближче до їх першого використання:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <iostream> int main() { std::cout << "Enter a number: "; int x; // ми використовуємо x в наступному рядку, тому оголошуємо цю змінну тут: якомога ближче до її першого використання std::cin >> x; // перше використання змінної x std::cout << "Enter another number: "; int y; // змінна y знадобилася нам лише тут, тому тут її і оголошуємо std::cin >> y; // перше використання змінної y std::cout << "The sum is: " << x + y << std::endl; return 0; } |
Такий стиль написання коду має декілька переваг.
По-перше, біля змінних, які оголошені якомога ближче до їх першого використання, знаходиться інший код, який сприяє кращому розумінню того, що відбувається. Якби змінна х
була оголошена в самому верху функції main(), то ми б не мали ні найменшого уявлення, для чого вона використовується, поки б не переглянули всю функцію цілком і не побачили б, де ж вона фактично використовується. Оголошення змінної х
біля операцій вводу/виводу даних дозволяє нам зрозуміти, що ця змінна використовується для вводу/виводу даних.
По-друге, оголошення змінних тільки там, де вони необхідні, повідомляє нам про те, що ці змінні не впливають на код вище, що робить програму простішою для розуміння.
І, нарешті, зменшується ймовірність випадкового створення неініціалізованих змінних.
У більшості випадків, ви можете оголосити змінну безпосередньо в рядку перед її першим використанням. Проте, іноді, можуть бути випадки, коли це не є бажаним (з міркувань продуктивності) або неможливо. Детальніше про це ми поговоримо в наступних уроках.
Правило: Оголошуйте змінні якомога ближче до їх першого використання.