Цей урок є більш детальним продовженням уроку №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: Копіююча ініціалізація (або “ініціалізація копіюванням”) за допомогою знаку рівності (=):
|
1 |
int nValue = 5; // копіююча ініціалізація |
Спосіб №2: Пряма ініціалізація за допомогою круглих дужок:
|
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(), то ми б не мали ні найменшого уявлення, для чого вона використовується, поки не переглянули всю функцію цілком і не побачили, де ж вона фактично використовується. Оголошення змінної х біля операцій вводу/виводу даних дозволяє нам зрозуміти, що ця змінна використовується для вводу/виводу даних.
По-друге, оголошення змінних тільки там, де вони необхідні, повідомляє нам про те, що ці змінні не впливають на код, розміщений вище, що робить програму простішою для розуміння.
І, нарешті, зменшується ймовірність випадкового створення неініціалізованих змінних.
У більшості випадків ви можете оголосити змінну безпосередньо в рядку перед її першим використанням. Проте, іноді, трапляються ситуації, коли це не є бажаним (з міркувань продуктивності) або неможливо. Детально про це ми поговоримо на наступних уроках.
Правило: Оголошуйте змінні якомога ближче до їх першого використання.
