Змінна (variable) - це засіб, який дозволяє програмістові тимчасово зберегти дані. Константа (constant) - це засіб, який дозволяє програмістові визначити елемент, якому не дозволено змінюватися.
На сьогоднішньому занятті.
■    Як в С++ 11 використати ключові слова auto і constexpr.
■    Як оголосити і визначити змінні і константи.
■    Як привласнювати значення змінним і маніпулювати ними.
■    Як вивести значення змінної на екран.

 Що таке змінна
Перш ніж перейти до розгляду потреби у використанні змінних в мові програмування, зробимо невеликий відступ і розглянемо, як комп'ютер містить і обробляє дані.
Коротко про пам'ять і адресацію

Усі комп'ютери, смартфони і інші програмовані пристрої мають мікропроцесор і певний об'єм пам'яті для тимчасового зберігання, що називається оперативною пам'яттю (Random Access Memory - RAM). Крім того, багато пристроїв дозволяють зберігати дані на довгостроковому пристрої, що запам'ятовує, такому як жорсткий диск. Мікропроцесор виконує ваше застосування і використовує при цьому оперативну пам'ять для його завантаження, а також для пов'язаних з ним даних, включаючи ті, які відображаються на екрані і вводяться користувачем.

Саму оперативну пам'ять, що є областю зберігання, можна порівняти з рядом шафок в гуртожитку, кожен з яких має свій номер, тобто адреса. Щоб отримати доступ до області в пам'яті, скажемо 578-й, процесору треба за допомогою специаль¬ний інструкції попросити вибрати звідти значення або записати значення в неї.

Оголошення змінних для діставання доступу і використання пам'яті

Наведені нижче приклади допоможуть зрозуміти, що таке змінні. Припустимо, ви пишете програму для множення двох чисел, що надаються користувачем. Пользо-вателя просять ввести множник і множене, один за іншим, і кожне з цих значень необхідно зберігати до моменту множення. Залежно від того, що ви хочете робити з результатом множення, їх може знадобиться зберігати і для пізнішого использо-вания в програмі.  Якби для зберігання чисел ви повинні були явно визначати адреси областей пам'яті (такий як 578), це було б повільно і схильно до помилок, оскільки ви повинні були б потурбуватися про відвертання перезапису даних, вже існую¬щих в цій області, і перезаписі ваших даних іншими згодом. При програмуванні на таких мовах, як C, для зберігання значень визначають змінні. Визначення змінної дуже просто і здійснюється за таким шаблоном:

тип_змінної  ім'я_змінної;

або

тип_змінної  ім'я_змінної = вихідне_значення;

Атрибут типу змінної вказує компілятору характер даних, які може зберігати змінна, і компілятор резервує для цього необхідний простір. Вибране програмістом ім'я змінної є більше осмисленою заміною для адреси області в пам'яті, де зберігається значення змінної. Якщо початкове значення не застосовується, ви не можете бути упевнені у вмісті цієї області пам'яті, що може бути погано для програми. Тому, будучи необов'язковою, ініціалізація частенько є хорошою практикою програмування.  Лістинг 3.1 демонструє оголошення змінних, їх ініціалізацію і використання в програмі, яка множить два числа, наданих користувачем.

ЛІСТИНГ 3.1  Використання змінних для зберігання чисел та результату їх множення

1: #include  
2: using namespace std;
3: 
4: int main ()
5: {
6:    cout << "This program will help you multiply two numbers"
              << endl;
7:
8:    cout << "Enter the first number: "; 
9:   int FirstNumber = 0; 
10:   cin >> FirstNumber;
11:
12:   cout << "Enter the second number: "; 
13:   int SecondNumber = 0; 
14:   cin >> SecondNumber;
15:
16:   // Множення двох чисел, зберігання результу в змінній 
17:   int MultiplicationResult = FirstNumber * SecondNumber;
18:
19:   // Відображення результату
20:   cout << FirstNumber << " x " << SecondNumber; 
21:   cout << " = " << MultiplicationResult << endl;
22:
23:   return 0;
24: }

Результат

This program will help you multiply two numbers
Enter the first number: 51
Enter the second number: 24
51 x 24 = 1224

Аналіз

   Це застосування просить користувача ввести два числа, результат множення яких і відображається. Щоб додаток міг використати введені користувачем числа, воно повинне зберігати їх в пам'яті. Змінні FirstNumber і SecondNumber, оголошений¬ные в рядках 9 і 13, вирішують задачу тимчасового зберігання введених користувачем ціло-чисельних значень. Оператори std : : ein в рядках 10 і 14 використовуються для набуття введених користувачем значень і збереження їх в двох цілочисельних змінних.  Оператор cout в рядку 21 використовується для відображення результату на консолі. Давайте проаналізуємо оголошення змінної детальніше:

9:    int FirstNumber = 0;

Цей рядок оголошує змінну типу int, який означає ціле число, на ім'я FirstNumber. В якості початкового змінній привласнюється нульове значення.

Таким чином, в порівнянні з програмуванням на асемблері, де необхідно явно просити процесор зберегти множник в області пам'яті, скажемо 578, мова C   

дозволяє звернутися до області пам'яті для збереження і отримання даних, використовуючи більше дружні концепції, такі як змінна на ім'я FirstNumber. Компілятор сам виконає завдання по зіставленню імені цією змінною з адресою області пам'яті і потурбується про відповідні дії за вас.

Таким чином, програміст працює із зрозумілими людині іменами, надаючи компілятору право перетворити змінну в адресу і створити інструкції для роботи мікропроцесора з оперативною пам'яттю.

УВАГА!

Імена змінних важливі для написання хорошого, зрозумілого і зручного у супроводі коду.Імена змінних можуть складатися з букв і цифр, але не можуть розпочинатися з цифр, а також містити пропуски і арифметичні оператори (такі як, - і так далі). Ви можете використати в іменах змінних символ підкреслення. Іменами змінних не можуть бути також зарезервовані ключові слова. Наприклад, змінна на ім'я return приведе до помилки при компіляції.

Оголошення і ініціалізація декількох змінних одного типу

   Змінні FirstNumber, SecondNumber і MultiplicationResult в лістингу 3.1 мають однаковий тип (ціле число), але оголошуються в трьох окремих рядках. По бажанню можна було б ущільнити оголошення цих трьох змінних до одного рядка кода, який виглядав би таким чином :

int FirstNumber = 0, SecondNumber = 0, MultiplicationResult = 0;

ПРИМІТКА  Як можна помітити, мова С++   дозволяє оголосити декілька змінних одного типу відразу і навіть оголошувати змінні на початку функції. Але все таки оголошення змінної перед її першим застосуванням частенько виявляється краще, оскільки це робить код більше читабельним і оголошення типу змінній виявляється ближче до місця її першого застосування.

УВАГА ! Дані, що зберігаються в змінних, знаходяться в оперативній пам'яті. Вони втрачаються при відключенні комп'ютера або завершенні роботи додатка, якщо програміст не збереже їх спеціально на такому носії даних, як жорсткий диск. Детальніше про збереження даних у файлі на диску ви дізнаєтеся на зайнятті 27, "Застосування потоків для введення і виведення".

Поняття зони видимості змінної

У звичайних змінних є чітко визначена зона видимості (scope), в межах якій вони допустимі і застосовні. При використанні поза своїми областями видимості імена змінних не будуть розпізнані компілятором, і ваша програма не буде відкомпільована. Поза своєю зоною видимості змінна - непізнана сутність, про яку компілятор нічого не знає.

Щоб краще зрозуміти концепцію зони видимості змінної, реорганізуємо про-грамму лістингу 3.1 у функцію MultiplyNumbers (), яка множить два числа і воз-вращает результат (лістинг 3.2).

ЛІСТИНГ 3.2  Демонстрація області видимості змінних

1: #include  
2: using namespace std;
3:
4: void MultiplyNumbers()
5: {
6:    cout << "Enter the first number: "; 
7:    int FirstNumber = 0; 
8:    cin >> FirstNumber;
9:
10:   cout << "Enter the second number: "; 
11:   int SecondNumber = 0; 
12:   cin >> SecondNumber;
13:
14:   // Множення двох чисел, зберігання результату в змінній
15:   int MultiplicationResult = FirstNumber * SecondNumber;
16:
17:   // Відображення результату
18:   cout << FirstNumber << " x " << SecondNumber; 
19:   cout << " = " << MultiplicationResult << endl;
20: }
21:  int main ()
22: {
23:         cout << "This program will help you multiply two numbers"
                 << endl;
24:
25:  // Вклик функції, яка виконує всю роботу
26:     MultiplyNumbers();
27:
28:  //cout << FirstNumber << " x " << SecondNumber; 
29:  //cout << " = " << MultiplicationResult << endl;
30:
31:  return 0;
32: }

РЕЗУЛЬТАТ

This program will help you multiply two numbers
Enter the first number: 51
Enter the second number: 24
51 x 24 = 1224

Аналіз

Код лістингу 3.2 робить те ж саме, що і код лістингу 3.1, створюючи те ж виведення. Єдина відмінність полягає в тому, що усі дії перенесені у функцію MultiplyNumbers (), що викликається функцією main (). Зверніть увагу на те, що змінні FirstNumber і SecondNumber не можуть використовуватися за межами функції MultiplyNumbers (). Якщо зняти коментар з рядка 28 або 29 у функції main (), то компіляція потерпить невдачу з найбільш вірогідною причиною undeclared identifier (неоголошений ідентифікатор).

Річ у тому, що змінні FirstNumber і SecondNumber мають локальну зону видимості і обмежуються тією функцією, в якій вони оголошені, в даному випадку функцією MultiplyNumbers (). Локальна змінна (local variable) застосовна у функції від місця її оголошення до кінця функції. Фігурна дужка (}), що означає кінець функції, означає також кінець зони видимості оголошених в ній змінних. Коли функція закінчується, усі її локальні змінні ліквідовуються, а займана ними пам'ять звільняється.

При компіляції оголошені в межах функції MultiplyNumbers () змінні ліквідовуються після закінчення функції, і якщо вони використовуються у функції main (), то відбувається помилка, оскільки змінні не були оголошені в ній.

УВАГА !!!  Якщо у функції main () оголосити інший набір змінних з тими ж іменами, то не сподівайтеся, що вони міститимуть те значення, яке, можливо, було присвоєно ним у функції MultiplyNumbers ().

Компілятор розглядає змінні у функції main () як незалежні сутності, навіть якщо їх імена співпадають з іменами змінних, оголошених в іншій функції

Глобальні змінні

Якби змінні, використовувані у функції MultiplyNumbers () лістингу 3.2, були оголошені не в ній, а поза нею, то вони були б придатні для використання і у функції main (), і у функції MultiplyNumbers (). Лістинг 3.3 демонструє глобальні змінні (global variable), що мають найширшу зону видимості в програмі.

ЛІСТИНГ 3.3.  Використання глобальних змінних

1:   #include <iostream>
2:   using namespace std;
3:
4:   // три глобальних цілих числа 
5: int FirstNumber = 0; 6: int SecondNumber = 0; 7: int MultiplicationResult = 0; 8: 9: void MultiplyNumbers () 10: { 11: cout << "Enter the first number: 12: cin >> FirstNumber; 13: 14: cout << "Enter the second number: "; 15: cin >> SecondNumber; 16: 17: // Множення двох чисел, зберігання результату в змінній 18: MultiplicationResult = FirstNumber * SecondNumber; 19: 20: // Вілображення результату 21: cout << "Displaying from MultiplyNumbers(): "; 22: cout << FirstNumber << " x " << SecondNumber; 23: cout << " = " << MultiplicationResult << endl; 24: } 25: int main () 26: { 27: cout << "This program will help you multiply two numbers" << endl; 28: 29: // Виклик функції, яка виконує всю работу 30: MultiplyNumbers(); 31: 32: cout << "Displaying from main(): "; 33: 34: // Тепер цей рядок компілюється і працює ! 35: cout << FirstNumber << " х " << SecondNumber; 36: cout << " = " << MultiplicationResult << endl; 37: 38: return 0; 39: }

РЕЗУЛЬТАТ

This program will help you multiply two numbers
Enter the first number: 51
Enter the second number: 19
Displaying from MyltiplyNumbers(): 51 x 19 = 969
Displaying from main(): 51 x 19 = 969

Аналіз

Лістинг 3.3 демонструє результат множення в двох функціях, причому змінні FirstNumber, SecondNumber і MultiplicationResult оголошені поза ними. Ці змінні глобальні (global), оскільки вони були оголошені в рядках 5-7, поза зоною видимості усіх функцій. Зверніть увагу на рядки 22 і 35, які використовують ці змінні і відображають їх значення. Зверніть особливу увагу на те, що змінна MultiplicationResult застосовується і у функції MultiplyNumbers (), і повторно у функції main ().

УВАГА !  Безпідставне використання глобальних змінних зазвичай вважається поганою практикою програмування.

Значення глобальної змінної може бути присвоєне у будь-якій функції, і це значення може виявитися непередбачувано, особливо якщо різні функціональні модулі розробляються різними програмістами групи.

Коректний спосіб отримання результату множення у функції main () лістингу 3.3 має на увазі повернення в неї результату виклику функціїMultiplyNumbers ().

Популярні типи змінних, які підтримуються компілятором C++

У більшості прикладів досі визначалися змінні типу int - тобто цілі числа. Проте у розпорядженні програмістів C++   є безліч фундаментальних типів змінних, підтримуваних самим компілятором. Вибір правильного типу змінною так само важливий, як і вибір правильних інструментів для роботи! Хрестоподібна викрутка не підійде для роботи з шурупом під плоску, так само і ціле число без знаку не може використовуватися для зберігання негативного значення! Типи змінних і характер даних, які вони можуть містити, приведені в таблицю. 3.1. Ця інформація дуже важлива при написанні ефективних і надійних програм C++.

ТАБЛИЦЯ 3.1 Типи змінних

Тип

Значення

bool

true(істина) або false(брехня)

char

256 символьних значень

unsigned short int

Від 0 до 65 535

short int

Від -32 768 до 32 767

unsigned long int

Від 0 до 4 294 967 295

long int

Від -2 147 483 648 до 2 147 483 647

unsigned long long

Від 0 до 18 446 744 073 709 551 615

long long

Від -9 223 372 036 854 775 808 до 9 223 372 036 854 775 807

int (16 біт)

Від -32 768 до 32 767

int (32 біта)

Від -2 147 483 648 до 2 147 483 647

unsignedint (16 біт)

Від 0 до 65 535

unsignedint (32 біта)

Від 0 до 4 294 967 295

float

Від 1.2е -38 до 3.4е38

double

Від 2.2е -308 до 1.8е308

Використання типу bool для зберігання логічних значень

Мова C++   надає тип, спеціально створений для зберігання логічних значе- ний true або false, обоє з яких є зарезервованими ключовими словами C++ . Цей тип особливо корисний при зберіганні параметрів і прапорів, які можуть бути встановлені або скинуті, існувати або бути відсутнім, можуть бути доступними або недоступними.

Типове оголошення логічної змінної, що ініціалізувала, має наступний вигляд:

bool AlwaysOnTop = false;

Вираз, який обробляє логічний тип, виглядає так:

bool DeleteFile = (UserSelection == "yes");

// істина, якщо тільки UserSelection включає "yes",

/ / в протилежному випадку брехня

Використання типу char для зберігання символьних значень

Тип char використовується для зберігання одного символу. Типове оголошення показане нижче.

char Userlnput = 'Y'; // ініціалізований символ 'Y'

Зверніть увагу, що пам'ять складається з бітів і байтів. Біти можуть містити значення 0 або 1, а байти можуть зберігати числові представлення, використовуючи ці біти. Таким чином, працюючи або привласнюючи символьні дані, як показано в прикладі, компілятор перетворить символи в числове представлення, яке може бути поміщене в пам'ять. Числове представлення латинських символів A - Z, a - z, чисел 0-9, деяких спеціальних клавіш (наприклад <DEL>) і спеціальних символів (таких, як повернення на один символ) було стандартизовано в стандартний американський код обміну інформацією (American Standard Code for Information Interchange), що називається також ASCII. Ви можете відкрити таблицю в застосуванні Д, "Коди ASCII", і побачити, що символ 'Y', присвоєний змінний Userlnput, - це десяткове значення 89, згідно із стандартом ASCII. Таким чином, компілятор просто зберігає значення 89 в області пам'яті, зарезервованої для змінної Userlnput.

Концепція знакових і беззнакових цілих чисел

Знак (sign) може означати позитивне або негативне число. Усі числа, з якими працює комп'ютер, зберігаються в пам'яті, як біти і байти. Область пам'яті розміром в 1 байт містить 8 бітів. Кожен біт може містити значення 0 або I (тобто зберігати одно з цих двох значень максимум). Таким чином, область пам'яті розміром в 1 байт може містити максимум 2 в ступені 8 значень, тобто 256 унікальних значень. Аналогічно область пам'яті розміром 16 бітів може містити 2 в ступені 16 значень, тобто 65 536 унікальних значень.

Якщо ці значення мають бути беззнаковими, щоб містити тільки позитивні значення, то один байт міг би містити цілочисельні значення в межах від 0 до 255, а два байти міститимуть значення в межах від 0 до 65 535 відповідно. Загляньте в таблицю. 3.1 і звернете увагу на те, що тип unsigned short int, який підтримує цей діапазон, займає в пам'яті 16 біт. Таким чином, позитивні значення у бітах і байтах дуже просто представити схематично (Рис. 3.1).

lesson 3.1

Але як же представити в цій області негативні числа? Один із способів - "пожертвувати" для знаку одним з розрядів, який вказував би, позитивне або негативне значення міститься в інших бітах (мал. 3.2). Знаковий розряд має бути самим старшим бітом (Most - Significant - Bit - MSB), оскільки самий молодший біт (Least - Significant - Bit - LSB) потрібний для позначення непарних чисел. Коли біт MSB містить інформацію про знак, передбачається, що значення 0 означає позитивне число, а значення 1 - негативне. Інші байти містять абсолютне значення.

lesson 3.2

Таким чином, що займає 8 бітів знакове число може містити значення в межах від - 128 до 127, а що займає 16 бітів - значення в межах від - 32 768 до 32 767. Ще раз подивитеся на таблицю. 3.1 і звернете увагу на те, що тип short int (знаковий) підтримує позитивні і негативні цілочисельні значення в 16-раз- рядному просторі.

Знакові цілочисельні типи short, int, long і long long

Ці типи відрізняються по своїх розмірах, а отже, і по діапазону значень, які вони можуть містити. Тип int, можливо, найпопулярніший тип розміром в 32 біта на більшості компіляторів. Використайте відповідний тип залежно від максимального значення, яке певна змінна імовірно міститиме.

Оголошення змінної знакового типу дуже просто:

short int SmallNumber = - 100;

int LargerNumber = - 70000;

long PossiblyLargerThanlnt = - 70000;

// на деяких платформах long співпадає c int long long LargerThanlnt = - 70000000000;

Беззнакові цілочисельні типи unsigned short, unsigned int, unsigned long і unsigned long long

На відміну від знакових аналогів, типи беззнакових цілочисельних змінних не можуть містити інформацію про знак, а отже, можуть містити удвічі більше позитивних значень.

Оголошення змінної без знакового типу теж дуже просто:

unsigned short int SmallNumber = 255;

unsigned int LargerNumber = 70000;

// на деякихплатформах long співпадає з int unsigned long PossiblyLargerThanlnt = 70000;

unsigned long long LargerThanlnt = 70000000000;

ПРИМІТКА

Беззнаковий тип змінної використовується тоді, коли очікуються тільки позитивні значення. Так, якщо ви підраховуєте кількість яблук, не використайте тип int, а використайте тип unsigned int. Останній може містити удвічі більше позитивних значень, чим перший.

УВАГА !

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

Типи з плаваючою точкою float і double

Числа з плаваючою комою ви, можливо, вивчали в школі як дійсні числа. Ці числа можуть бути позитивними і негативними. Вони можуть містити десяткові значення. Так, якщо в змінній C   необхідно зберегти значення числа Пі (22/7, або 3,14), ви використали б для неї тип з плаваючою точкою.

Оголошення змінних цих типів наслідує той же шаблон, що і тип int в лістингу 3.1. Так, змінна типу float, що дозволяє зберігати десяткові значення, могла б бути оголошена таким чином:

float Pi = 3.14;

Змінна речового типу з подвійною точністю (чи типу double) визначається так:

double MorePrecisePi = 22 / 7;

ПРИМІТКА

Згадані в таблиці типи даних частенько називають простими старими даними (Plain Old Data - POD). До цієї категорії відносяться також об'єднання цих типів (структури, перерахування, об'єднання і класи).

Визначення розміру змінною з використанням оператора sizeof

Розмір (size) - це об'єм пам'яті, що резервується компілятором при оголошенні програмістом змінної для утримання привласнюваних їй даних. Розмір змінної залежить від її типу, і в мові C   є дуже зручний оператор sizeof, який повідомляє розмір у байтах змінної або типу.

Застосування оператора sizeof дуже просто. Щоб визначити розмір цілого числа, викликайте оператор sizeof з параметром int (тип), як показано в лістингу 3.4.

cout << "Size of an int: " << sizeof (int);

ЛІСТИНГ 3.4. Пошук розміру стандартних типів змінних мови C++

1:   #include 
2: 
3:   int main()
4:   {
5:   using namespace std;
6:   cout << "Computing the size of some C++ inbuilt variable types" 
        << endl;
7: 
8:   cout << "Size of bool: " << sizeof(bool) << endl;
9:   cout << "Size of char: " << sizeof(char) << endl;
10: cout << "Size of unsigned short int: " << sizeof(unsigned short) 
        << endl;
11: cout << "Size of short int: " << sizeof(short) << endl; 
12: cout << "Size of unsigned long int: " << sizeof(unsigned long)
         << endl;
13: cout << "Size of long: " << sizeof(long) << endl; 
14: cout << "Size of int: " << sizeof(int) << endl; 
15: cout << "Size of unsigned long long: "
         << sizeof(unsigned long long)<< endl; 
16: cout << "Size of long long: " << sizeof(long long) << endl;
17: cout << "Size of unsigned int: " << sizeof(unsigned int) << endl;
18: cout << "Size of float: " << sizeof(float) << endl;
19: cout << "Size of double: " << sizeof(double) << endl;
20:
21: cout << "The output changes with compiler, hardware and OS"
         << endl;
22:
23: return 0;
24:   }

Результат

Computing the size of some C inbuilt variable types
Size of bool: 1
Size of char: 1
Size of unsigned short int: 2
Size of short int: 2
Size of unsigned long int: 4
Size of long: 4
Size of int: 4
Size of unsigned long long: 8 Size of long long: 8 Size of unsigned int: 4 Size of float: 4 Size of double: 8

Конкретні значення виведення залежать від компілятора, апаратних засобів і операційної системи.

Аналіз

Виведення лістингу 3.4 демонструє розміри різних типів у байтах і залежить від конкретної платформи: компілятора, операційної системи і апаратних засобів. Це конкретне виведення - це результат виконання програми в 32-бітовому режимі (32-бітовий компілятор) на 64-бітовій операційній системі. Зверніть увагу, що 64-бітовий компілятор, ймовірно, дасть інші результати, а 32-бітовий компілятор автор вибрав тому, що повинен був мати можливість запускати застосування як на 32-бітових, так і на 64-бітових системах. Виведення оператора sizeof свідчить про те, що розмір змінної знакового і беззнакового типу однаковий; єдиною відмінністю у цих двох типів MSB є наявність інформації про знак.

ПРИМІТКА

Усі розміри у виведенні приведені у байтах. Розмір об'єкту - важливий параметр при резервуванні пам'яті для неї, особливо коли резервування здійснюється динамічно.

ЛІСТИНГ 3.5. Використання ключового слова auto для виведення типів компілятором

1:  #include  
2:  using namespace std;
3:
4:  int main()
5:  {
6:      auto Flag = true;
7:      auto Number = 2500000000000;
8:
9:      cout « "Flag = " « Flag;
10:     cout « " , sizeof(Flag) = " « sizeof(Flag) << endl; 
11:     cout « "Number = " « Number;
12:     cout « " , sizeof(Number) = " « sizeof(Number) « endl; 
13:  
14:     return 0;
15:  }

Результат

Flag = 1, sizeof (Flag) = 1

Number = 2500000000000, sizeof (Number) = 8

Аналіз

Як можна помітити, замість явної вказівки типу bool для змінної Flag і типу long long для змінної Number в рядках 6 і 7, де вони оголошуються, було використано ключове слово auto. Це делегує рішення про тип змінних компілятору, який використовує для цього значення, що ініціалізувало. Щоб перевірити, чи створив компілятор фактично передбачувані типи, використовується оператор sizeof, що дозволяє побачити у виведенні лістингу 3.4, що це дійсно так.

ПРИМІТКА   Використання ключового слова auto вимагає ініціалізації змінної, оскільки компілятор потребує значення, що ініціалізувало, щоб прийняти рішення про найкращий тип для змінної.

Якщо ви не ініціалізували змінну, то застосування ключового слова auto приведе до помилки при компіляції.

Хоча на перший погляд ключове слово auto здається не особливо корисним, воно істотно спрощує програмування в тих випадках, коли тип змінної складений. Візьмемо, наприклад, випадок, коли оголошується динамічний масив цілих чисел MyNumbers у формі std:: vector:

std::vector<int> MyNumbers;

Ви звертаєтеся до елементів масиву (чи перебираєте їх) і відображаєте отримані значення, використовуючи наступний код:

for ( vector<int>::const _ iterator Iterator = MyNumbers.begin ();

Iterator < MyNumbers.end ();

Iterator )

cout " * Iterator << " ";

Ні тип std : .-vector, ні оператор циклу for ще не були пояснені, тому не хвилюйтеся, якщо код здається незрозумілим. Він лише перебирає усі елементи вектору, починаючи з початку до кінця, і відображає їх значення за допомогою оператора cout. Подивіться на складність першого рядка, де оголошується змінна Iterator і їй привласнюється початкове значення, повернене методом begin (). Змінна Iterator має тип vector<int>:: const _ iterator, вивчати і писати який програмістам дуже складно. Замість того, щоб знати її напам’ять, програміст може довірившись типу значення, яке повертається методу begin() і спростити код циклу for до слідуючого:

for ( auto Iterator = MyNumbers.begin ();

      Iterator < MyNumbers.end ();

      ++Iterator )

      cout << * Iterator "  ";

Зверніть увагу, наскільки коротше став перший рядок. Компілятор перевіряє значення змінної Iterator, що ініціалізувало, яким є повертальне значення методу begin (), і призначає його як тип змінної. Це спрощує програмування на мові C, особливо коли ви використовуєте шаблони не дуже часто.

Використання ключового слова typedef для заміни типу змінною

Мова C++   дозволяє перейменовувати типи змінних в щось, що ви могли б знайти зручнішим. Для цього використовується ключове слово typedef. Наприклад, програміст хоче призначити типу unsigned int більше описове ім'я STRICTLY _ POSI - TIVE _ INTEGER.

typedef unsigned int STRICTLY _ POSITIVE _ INTEGER;

STRICTLY _ POSITIVE _ INTEGER PosNumber = 4532;

При компіляції перший рядок вказує компілятору, що STRICLY _ POSITIVE _ IN - TEGER - ця тільки назва типу unsigned int. Згодом, коли компілятор зустрічає вже певний тип STRICLY _ POSITIVE _ INTEGER, він замінює його типом unsigned int і продовжує компіляцію.

ПРИМІТКА

Визначення або підстановка типу особливо зручна при роботі із складними типами, у яких може бути громіздкий синтаксис. Наприклад, при використанні шаблонів.

Що таке константа

Припустимо, ви пишете програму для обчислення площі і периметра круга. Формули такі:

Площа = Pi * Радіус * Радіус;

Периметр = 2 * Pi * Радіус круга

У цих формулах Pi - це константа зі значенням 22/71. Ви не хочете, щоб значення Pi змінилося де-небудь у вашій програмі. Ви також не хочете випадково присвоїти Pi неправильне значення, скажімо, при недбалому копіюванні і вставці або при контекстному пошуку і заміні. Мова C   дозволяє визначити Pi як константу, яка не може бути змінена після оголошення. Іншими словами, після того, як значення константи визначене, воно не може бути змінене. Спроби привласнення значення константі в мові C   призводять до помилки при компіляції.

Таким чином, в C++   константи схожі на змінні, за винятком того, що вони не можуть бути змінені. Подібно до змінних, константи також займають простір в пам'яті і мають ім'я для ідентифікації адреси зарезервованої для неї області. Проте вміст цієї області не може бути перезаписаний. У мові C   можливі наступні константи.

■ Літеральні константи.

■ Константи, оголошені з використанням ключового слова const.

■ Константні вирази, що використовують ключове слово constexpr (нововведення c++).

■ Перераховувані константи, що використовують ключове слово enum.

■ Певні константи, використання яких не рекомендується і засуджується.

Літеральні константи

Повернемося до лістингу 3.1 - простій програмі, що множить два числа. Цілочисельна змінна на ім'я FirstNumber там оголошувалася так:

9: int FirstNumber = 0;

Цілочисельній змінній FirstNumber привласнюється початкове нульове значення. Тут нуль - це частина кода, компільована в застосування, вона є незмінною і називається літеральною константою (literal constant). Літеральні константи бувають безліч типів : bool, integer, string і так далі. У найпершій програмі C   (див. лістинг 1.1) текст "Hello World" відображався з використанням наступного коду:

std::cout << "Hello World" << std::endl;

де "Hello World" - це константа строковий літерал (string literal).

Оголошення змінних як констант з використанням ключового слова const

Найважливіший тип констант C++, з практичною і програмною точок зору, оголошується за допомогою ключового слова const, розташованого перед типом змінної. У загальному вигляді оголошення виглядає таким чином:

const ммя_тмпа имя_констатны;

Давайте розглянемо просте застосування, яке відображає значення константи на ім'я Pi (лістинг 3.6).

ЛІСТИНГ 3.6. Оголошення константи на ім'я Pi

1:   #include 
2:
3:   int main()
4:   {
5:       using namespace std;
6:
7:       const double Pi = 22.0 / 7;
8:       cout << "The value of constant Pi is: " << Pi << endl;
9:
10:      // Снятие комментария со следующей строки приведет к ошибке
11:      // Pi = 345;
12:
13:      return 0;
14:  }

Результат

The value of constant Pi is: 3.14286

Аналіз

Зверніть увагу на оголошення константи Pi в рядку 7. Ключове слово const дозволяє вказати компілятору, що Pi - це константа типу double. Якщо зняти коментар з рядка 11, де здійснюється спроба присвоїти значення змінної, яку ви визначили як константу, станеться помилка при компіляції приблизно з таким повідомленням: You cannot assign to a variable that is const (Ви не можете присвоїти значення змінної, яка є константою). Таким чином, константи - це прекрасний спосіб гарантувати незмінність певних даних.

ПРИМІТКА   Хорошою практикою програмування є визначення змінних, значення яких передбачаються незмінними, як констант. Застосування ключового слова const гарантує, що програміст потурбувався про забезпечення незмінності даних і гарантує своє застосування від неумисних змін цієї константи.

Це особливо корисно там, де програмістів декілька.

Константи корисні при оголошенні масивів постійної довжини, які незмінні під час компіляції. Лістинг 4.2, приведений в зайнятті 4, "Масиви і рядки", містить приклад, що демонструє використання синтаксису const int при визначенні довжини масиву.

Оголошення констант з використанням ключового слова constexpr

Концепція константних виразів (constant expression) завжди існувала в мові C, ще до появи З 11, проте вона не була формалізована в ключове слово. Зверніть увагу на приклад коду в лістингу 3.5, в якому константне вираження 22.0 / 7 цілком підтримується компіляторами і до 2011 року випуску. Проте попередні компілятори не враховували визначення функцій, які могли бути виконані під час компіляції. У мові З 11 ви можете визначити це так:

constexpr double GetPiO {return 22.0 / 7;}

Оголошення функції Get Pi (), використовуваної в комбінації з іншою константою, такою, як тут, робить допустимим наступний оператор:

constexpr double TwicePiO {return 2 * GetPi () ; }

Відмінність між константою і константним вираженням на перший погляд трохи; проте з точки зору компілятора і застосування - це нові можливості по оптимізації. Другий оператор (без constexpr) після компіляції колишнім компілятором оброблявся б застосуванням під час виконання, а компілятор, сумісний

із стандартом С++11, обробив би вираз під час компіляції, і застосування виконувалося б швидше.

ПРИМІТКА

На момент написання цієї книги ключове слово constexpr не підтримувалося компілятором Microsoft Visual C++   Express Compiler. Воно підтримувалося тільки компілятором g++ .

Константи, які перераховуються

Іноді деяка змінна може набувати значення тільки з певного набору. Наприклад, ви не хочете, щоб серед барв веселки випадково виявився бірюзовий або серед напрямів компаса виявився напрям вліво. У обох цих випадках потрібний тип змінної, значення якої обмежуються визначеним вами набором. Перераховувані константи (enumerated constant) - це саме те, що необхідно в цій ситуації, і характеризуються вони ключовим словом enum.

Ось приклад перераховуваної константи, яка визначає барви веселки :

enum RainbowColors

{

   Violet = О

   Indigo

   Blue

   Green

   Yellow

   Orange

   Red

};

А ось інший приклад - напрям компаса :

enum CardinalDirections

{

     North

     South

     East

     West

};

Зверніть увагу, що ці перераховувані константи можуть тепер використовуватися як типи змінних, які можуть набувати значень, обмежених оголошеним раніше утримуваним. Так, при визначенні змінної, яка містить барви веселки, ви оголосили б її так:

RainbowColors MyWorldsColor = Blue; // Початкове значення

У приведеному вище рядку коду оголошується перераховувана змінна MyWorldsColor типу RainbowColors. Ця перераховувана змінна обмежена змістом тільки однієї з семи барв веселки, і не допускає ніяких інших значень.

ПРИМІТКА

При оголошенні перераховуваної константи компілятор перетворить перераховувані значення, такі як violet і інші, в цілі числа. Кожне подальше значення перерахування виявляється на одиницю більше за попередній. Початкове значення ви можете задати самі, але якщо не зробите цього, то компілятор почне з 0. Так, значенню North відповідає числове значення 0.

За бажанням можна також явно визначити числове значення навпроти кожної з перераховуваних констант при їх ініціалізації.

Лістинг 3.7 демонструє використання перераховуваних констант для утримання чотирьох напрямів при ініціалізації першого значення.

ЛІСТИНГ 3.7. Використання перераховуваних значень для вказівки напрямів вітру

1:   #include 
2:   using namespace std;
3:
4:   enum CardinalDirections 
5:   {
6:        North = 25,
7:        South,
8:        East,
9:        West
10:  };
11:
12:  int main()
13:  {
14:       cout << "Displaying directions and their symbolic values"
                   << endl;
15:       cout << "North: " << North << endl; 		 
16:       cout << "South: " << South << endl;  
17:       cout << "East: " << East << endl; 
18:       cout << "West: " << West << endl;
19:
20:       CardinalDirections WindDirection = South;
21:       cout << "Variable WindDirection = " << WindDirection << endl;
22: 
23:       return 0;
24:   }

Результат

Displaying directions and their symbolic values

North: 25

South: 26

East: 27

West: 28

Variable WindDirection = 26

Аналіз

Зверніть увагу на те, як визначені чотири перераховувані константи напряму, але початкове значення 25 було присвоєно тільки першому, North (див. рядок 6). Це автоматично гарантує, що наступним константам відповідатимуть значення 26, 27 і 28, як представлено у виведенні. У рядку 20 створюється змінна типу Cardi - nalDirections, якій привласнюється початкове значення South. При виведенні на екран в рядку 21 компілятор передає цілочисельне значення, пов'язане зі значенням South, яким є 26.

ПОРАДА

Має сенс звернутися до лістингів 6.4 і 6.5 зайняття 6, "Галуження процесу виконання програм". Там перерахування використовується для днів тижня, а умовне вираження дозволяє вказати вибраний користувачем день.

Визначення констант з використанням директиви #define

Головне, не використайте це при написанні нових програм. Єдина причина згадки визначення констант з використанням директиви #def ine в цій книзі полягає в тому, щоб допомогти вам зрозуміти деякі застарілі програми, в яких для визначення числа Пі міг би використовуватися такий синтаксис :

#define Pi 3.14286

Це макрокоманда препроцесора, приписуюча компілятору замінювати усі згадки Pi значенням 3.14286. Зверніть увагу: це текстова заміна (читай: неінтелектуальна), здійснювана препроцесором. Компілятор не знає і не піклується про фактичний тип даної константи.

УВАГА! Визначення констант з використанням директиви препроцесора #def ine засуджується і не рекомендується.

Іменування змінних і констант

Існує безліч різних способів і угод по іменуванню змінних. Деякі програмісти вважають за краще приписувати до імен змінних декілька символів, що означають тип. Наприклад:

bool blsLampOn = false;

де b - префікс, який програміст додав для вказівки на те, що змінна має тип bool. Подібна форма запису називається Угорською нотацією; спочатку вона була розроблена і застосована корпорацією Microsoft. Проте мова C   - строго типізується, і компілятор сам знає тип змінної, причому не по префіксу в її імені, а за типом в її оголошенні, яким є bool. Таким чином, програмістам нині настійно не рекомендується наслідувати Угорську нотацію. Ім'я змінної має бути зрозумілим, навіть якщо воно стане трохи довгим.

Враховуючи, що логічна змінна в цьому прикладі використовувалася при програмуванні електроустаткування автомобіля, трохи кращим варіантом буде наступний:

bool IsHeadLampOn = false;

Помітимо, що і ці варіанти кращі і зрозуміліші, ніж щось на зразок наступного:

bool b = false;

Таких неінформативних імен змінних треба уникати за всяку ціну.

РЕКОМЕНДУЄТЬСЯ 

Присвоюйте змінним осмислені імена, навіть якщо вони стають довгими Упевніться, що ім'я змінної пояснює її мету

Переконайтесь на місце того, хто ще не бачив ваш код, і подумайте, чи мають сенс вживані в ній імена

Використовуйте у своїй групі угоду про іменування і строго дотримуйтеся його

НЕ РЕКОМЕНДУЄТЬСЯ

Не присвоюйте змінним імена, які занадто короткі або містять тільки один символ

Не присвоюйте змінним імена, які використовують екзотичні акронимы, відомі тільки вам

Не присвоюйте змінним імена, співпадаючі із зарезервованими ключовими словами мови C++, оскільки такий код не компілюватиметься

Ключові слова, неприпустимі для використання як імен змінних і констант

Деякі слова зарезервовані мовою C++, і їх не можна використати в якості імен змінних. У цих ключових слів є спеціальне значення для компілятора C++ . До ключових відносяться такі слова, як if, while, for і main. Список ключових слів мови C++   приведений в таблицю. 3.2, а також в застосуванні б, "Ключові слова мови C++ ". У вашого компілятора можуть бути додаткові зарезервовані слова, тому для повноти списку перевірте його документацію.

ТАБЛИЦА 3.2.  Ключові слова C++

asm

else

New

this

auto

enum

Operator

throw

bool

explicit

Private

true

break

export

Protected

try

case

extern

Public

typedef

catch

false

Register

typeid

char

float

reinterpret_cast

typename

class

for

Return

union

const

friend

Short

unsigned

const cast

goto

Signed

using

continue

if

Sizeof

virtual

defolt

inline

Static

void

delete

int

static_cast

volatile

do

long

Struct

wchar_t

double

mutable

Switch

while

dynamic_cast

namespace

Template

 

   Крім цього зарезервовані наступні слова:

and

bitor

not_eq

xor

and_eq

compl

Or

xor_eq

bitand

not

or_eq

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Резюме

На цьому зайнятті йшлося про використання пам'яті для тимчасового зберігання значень в змінних і константах. Ви дізналися, що розмір змінних визначає їх тип і що оператор sizeof дозволяє з'ясувати його. Ви дізналися про існування різних типів змінних, таких як bool, int і так далі, а також про те, що вони повинні використовуватися для утримання цих різних типів. Правильний вибір типу змінної важливий для ефективності програми; якщо для змінної вибраний занадто маленький тип, це може закінчитися помилкою оболонки або переповнюванням регістрів. Ви познайомилися з новим ключовим словом C 11 auto, який дозволяє компілятору самостійно вивести тип даних на основі значення змінної, що ініціалізувало. Крім того, ми розглянули різні типи констант і застосування найважливіших з них (що використовують ключові слова const і enum).

■ Навіщо взагалі визначати константи, якщо замість них можна використати звичайні змінні?

Константи, особливо ті, в оголошенні яких використовується ключове слово const, є засобом вказати компілятору, що значення певної змінної має бути постійним і не повинно змінюватися. Отже, компілятор гарантує, що змінною, оголошеною постійною, ніколи не буде присвоєно інше значення, навіть якщо інший програміст, поправляючи вашу роботу, з необережності спробує перезаписувати значення. Так, якщо ви знаєте, що значення змінної не повинне змінюватися, хороша практика програмування має на увазі оголошення її як константи, що збільшує якість застосування. ■ Навіщо ініціалізувати значення змінної?

Якщо ви не ініціалізували змінну, то не знаєте, яке значення вона містить спочатку. Початкове значення - це тільки утримуване області пам'яті, зарезервованої для змінної. Ініціалізація змінної, така як

int MyFavoriteNumber = 0;,

записує в область пам'яті, зарезервованої для змінної MyFavoriteNumber, початкове значення по вашому вибору, в даному випадку 0. нерідкі ситуації, коли здійснюється обробка умовного вираження залежно від значення змінної (як правило, перевірка на відмінність від нуля). Без ініціалізації така логіка працює ненадійно, оскільки знову зарезервована область пам'яті містить те, що в ній було раніше, тобто випадкове значення, яке частенько відрізняється від нуля.

■ Чому мова C   дозволяє використати для цілих чисел різні типи: short int, int і long int? Чом би не використати завжди тільки той тип, який дозволяє зберігати найбільші значення?

Мова програмування C   використовується для розробки безлічі застосувань, деякі з яких виконуються на пристроях з невеликими обчислювальними можливостями і ресурсами пам'яті. Простий старий стільниковий телефон - один з прикладів, де обчислювальні можливості і доступна пам'ять дуже обмежені. В даному випадку програміст може заощадити пам'ять і прискорити виконання, вибравши правильний тип змінної, якщо він не потребує великих значень. Якщо програма призначена для робочого столу або високопродуктивного смартфону, економія пам'яті і збільшення продуктивності за рахунок вибору типу одного цілого числа буде незначною, а в деяких випадках - навіть бути відсутнім. ■ Чому не слід широко використати глобальні змінні? Хіба не правда, що вони придатні для використання всюди в застосуванні, і я можу заощадити час на передачі значень у функції і з них?

Значення глобальних змінних можна читати і привласнювати глобально. Останнє і є проблемою, оскільки вони можуть бути змінені глобально. Припустимо, ви працюєте над проектом разом з декількома програмістами. Ви оголосили свої цілочисельні і інші змінні глобальними. Якщо програміст вашої групи змінює значення цілого числа у своєму коді, який може навіть знаходитися не в тому файлі . дрр, який використовуєте ви, на ваш код це теж вплине. Тому економія декількох секунд або хвилин не має бути критерієм, і ви не повинні використати глобальні змінні без розбору, щоб гарантувати стабільність свого коду. ■ Мова C   дозволяє оголошувати беззнакові цілочисельні змінні, які, як передбачається, здатні містити тільки позитивні цілочисельні значення і нуль. Що станеться при декременті нульового значення змінної типу unsigned int?

Станеться переповнювання (wrapping). Декремент значення 0 беззнаковій цілочисельній змінній на 1 перетворить його на найвище значення, яке вона здатна містити! Подивіться таблицю. 3.1 і переконаєтеся, що змінна типу unsigned short здатна містити значення від 0 до 65535. Отже, оголосимо змінну типу unsigned short, здійснимо декремент і побачимо дещо несподіване :

unsigned short MyShortlnt =0; // Початкове значення

MyShortlnt = MyShortlnt - 1; // Декремент на 1

std::cout << MyShortlnt " std::endl; // Висновок: 65535!

Зверніть увагу: це проблема не типу unsigned short, а вашого способу її застосування. Цілочисельний тип без знаку (короткий або довгий) не повинен використовуватися там, де очікується наявність негативних значень. Якщо вміст змінної MyShortlnt повинен використовуватися для кількості динамічно резервованих байтів, то невелика помилка, що допустила декремент нульового значення, привела б до резервування 64 Кбайт! Гірше за те, якби змінна MyShortlnt використовувалася як індекс при доступі до областей пам'яті, ваше застосування, найвірогідніше, звернулося б до зовнішньої області пам'яті і потерпіло невдачу!