У C++ є два види констант: літеральні і символьні. У цьому уроці ми розглянемо літеральні константи.
Літеральні константи
Літеральні константи (або просто “літерали”) — це значення, які вставляються безпосередньо в код. Оскільки вони є константами, то їх значення змінити не можна, наприклад:
1 2 3 |
bool myNameIsAlex = true; // true - це літеральна константа типу bool int x = 5; // 5 - це літеральна константа типу int int y = 2 * 3; // 2 і 3 - це літеральні константи типу int |
З літералами типів bool і int все зрозуміло, а ось з літералами типу з плаваючою крапкою є два способи оголошення:
1 2 |
double pi = 3.14159; // 3.14159 - це літерал типу double double avogadro = 6.02e23; // число avogadro - 6.02x10^23 |
У другому способі оголошення, число після експонента може бути і від’ємним:
1 |
double electron = 1.6e-19; // заряд електрона - 1.6x10^-19 |
Числові літерали можуть мати суфікси, які визначають їх типи. Ці суфікси не є обов’язковими, так як компілятор розуміє з контексту, константу якого типу даних ви хочете використовувати.
Тип даних | Суфікс | Значення |
int | u чи U | unsigned int |
int | l чи L | long |
int | ul, uL, Ul, UL, lu, lU, Lu чи LU | unsigned long |
int | ll чи LL | long long |
int | ull, uLL, Ull, ULL, llu, llU, LLu чи LLU | unsigned long long |
double | f чи F | float |
double | l чи L | long double |
Суфікси є навіть для цілочисельних типів (але вони майже не використовуються):
1 2 |
unsigned int nValue = 5u; // тип int unsigned long nValue2 = 5L; // тип long |
За замовчуванням літеральні константи типу з плаваючою крапкою є типу double. Для конвертації літеральних констант в тип float, можна використовувати суфікс f
або F
:
1 2 |
float fValue = 5.0f; // тип float double d = 6.02e23; // тип double (за замовчуванням) |
C++ також підтримує літерали типів string і char:
1 2 3 |
char c = 'A'; // 'A' - це літерал типу char std::cout << "Hello, world!"; // "Hello, world!" - це літерал рядка C-style std::cout << "Hello," " world!"; // C++ пов'язує послідовні літерали типу string |
Літерали добре використовувати в коді доти, доки їх значення зрозумілі й однозначні. Це виконання операцій присвоювання, математичних операцій або виведення тексту в консоль.
Літерали у вісімковій і шістнадцятковій системах числення
У повсякденному житті ми використовуємо десяткову систему числення, яка складається з десяти цифр: 0, 1, 2, 3, 4, 5, 6, 7, 8 і 9. За замовчуванням C++ використовує десяткову систему числення для чисел в програмах:
1 |
int x = 12; // мається на увазі, що 12 є числом десяткової системи числення |
У двійковій (бінарній) системі числення всього 2 цифри: 0 і 1. Значення: 0, 1, 10, 11, 100, 101, 110, 111 і т.д.
Є ще дві інші системи числення: вісімкова і шістнадцяткова.
Вісімкова система числення складається з 8 цифр: 0, 1, 2, 3, 4, 5, 6 і 7. Значення: 0, 1, 2, 3, 4, 5, 6, 7, 10, 11, 12 і т.д. (примітка: цифр 8 і 9 — немає, так що відразу перескакуємо з 7 до 10).
Десяткова система числення | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
Вісімкова система числення | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 |
Для використання літералу з вісімкової системи числення, використовуйте префікс 0
(нуль):
1 2 3 4 5 6 7 8 |
#include <iostream> int main() { int x = 012; // 0 перед значенням означає, що це вісімковий літерал std::cout << x; return 0; } |
Результат виконання програми:
10
Чому 10 замість 12? Тому що std::cout виводить числа в десятковій системі числення, а 12 в вісімковій системі числення = 10 в десятковій системі числення.
Вісімкова система числення використовується дуже рідко.
Шістнадцяткова система числення складається з 16 символів: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, А, В, С, D, Е, F.
Десяткова система числення | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
Шістнадцяткова система числення | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | A | B | C | D | E | F | 10 | 11 |
Для використання літералу з шістнадцяткової системи числення, використовуйте префікс 0x
:
1 2 3 4 5 6 7 8 |
#include <iostream> int main() { int x = 0xF; // 0x перед значенням означає, що це шістнадцятковий літерал std::cout << x; return 0; } |
Результат виконання програми:
15
Оскільки в цій системі 16 символів, то одна шістнадцяткова цифра займає 4 біта. Отже, дві шістнадцяткові цифри займають 1 байт.
Розглянемо 32-бітне ціле число з двійкової системи числення: 0011 1010 0111 1111 1001 1000 0010 0110
. Через довжину і повторення цифр його складно прочитати. У шістнадцятковій системі числення це ж значення буде виглядати наступним чином: 3A7F 9826
. Такий зручний/стислий формат є перевагою шістнадцятковї системи числення. Тому шістнадцяткові значення часто використовуються для представлення адрес пам’яті або необроблених значень в пам’яті.
До C++14 використовувати літерал з двійкової системи числення було неможливо. Проте шістнадцяткова система числення може нам в цьому допомогти:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include <iostream> int main() { int bin(0); bin = 0x01; // присвоюємо змінній бінарний літерал 0000 0001 bin = 0x02; // присвоюємо змінній бінарний літерал 0000 0010 bin = 0x04; // присвоюємо змінній бінарний літерал 0000 0100 bin = 0x08; // присвоюємо змінній бінарний літерал 0000 1000 bin = 0x10; // присвоюємо змінній бінарний літерал 0001 0000 bin = 0x20; // присвоюємо змінній бінарний літерал 0010 0000 bin = 0x40; // присвоюємо змінній бінарний літерал 0100 0000 bin = 0x80; // присвоюємо змінній бінарний літерал 1000 0000 bin = 0xFF; // присвоюємо змінній бінарний літерал 1111 1111 bin = 0xB3; // присвоюємо змінній бінарний літерал 1011 0011 bin = 0xF770; // присвоюємо змінній бінарний літерал 1111 0111 0111 0000 return 0; } |
Бінарні літерали і розділювач цифр в C++14
В C++14 ми можемо використовувати бінарні (двійкові) літерали, додаючи префікс 0b
:
1 2 3 4 5 6 7 8 9 10 11 12 |
#include <iostream> int main() { int bin(0); bin = 0b1; // присвоюємо змінній бінарний літерал 0000 0001 bin = 0b11; // присвоюємо змінній бінарний літерал 0000 0011 bin = 0b1010; // присвоюємо змінній бінарний літерал 0000 1010 bin = 0b11110000; // присвоюємо змінній бінарний літерал 1111 0000 return 0; } |
Оскільки довгі літерали читати важко, то в C++14 додали можливість використовувати одинарну лапку '
як розділювач цифр:
1 2 3 4 5 6 7 8 9 |
#include <iostream> int main() { int bin = 0b1011'0010; // присвоюємо змінній бінарний літерал 1011 0010 long value = 2'532'673'462; // набагато простіше читати, ніж 2532673462 return 0; } |
Якщо ваш компілятор не підтримує C++14, то використовувати бінарні літерали і розділювач цифр ви не зможете — компілятор видасть помилку.
Магічні числа. Що з ними не так?
Розглянемо наступний фрагмент коду:
1 |
int maxStudents = numClassrooms * 30; |
В наведеному вище прикладі число 30
є магічним числом. Магічне число — це добре закодований літерал (зазвичай, число) в рядку коду, який не має ніякого контексту. Що це за 30
, що воно означає/позначує? Хоча з прикладу вище можна здогадатися, що число 30
означає максимальну кількість учнів, які перебувають в одному кабінеті, в більшості випадків, це не завжди буде настільки очевидним і зрозумілим. У більш складних програмах контекст подібних чисел розгадати набагато складніше (якщо тільки не буде відповідних коментарів).
Використання магічних чисел є поганою практикою, так як в доповнення до того, що вони не надають ніякого контексту (для чого і чому використовуються), вони також можуть створювати проблеми, якщо їх значення необхідно буде змінити. Припустимо, що школа закупила нові парти, що, відповідно, збільшило максимальну кількість учнів, які перебувають в одному кабінеті, з 30 до 36 — це потрібно буде продумати і відобразити в нашій програмі.
Розглянемо наступний фрагмент коду:
1 2 |
int maxStudents = numClassrooms * 30; setMax(30); |
Щоб оновити число учнів в кабінеті, нам потрібно змінити значення константи з 30
на 36
. Але що робити з викликом функції setMax(30)? Аргумент 30
і константа 30
в коді вище є одним і тим же, вірно? Якщо так, то нам потрібно буде оновити це значення. Якщо ні, то нам не слід взагалі чіпати цей виклик функції. Якщо ж проводити автоматичний глобальний пошук і заміну числа 30
, то можна ненароком змінити і аргумент функції setMax(), в той час, коли його взагалі не слід було б чіпати. Тому вам доведеться переглянути весь код “вручну”, в пошуках числа 30
, а потім, в кожному конкретному випадку, визначитись: змінювати 30
на 36
чи ні. Це може зайняти дуже багато часу + ймовірність виникнення нових помилок підвищується в рази.
На щастя, є кращий варіант — використовувати символьні константи. Про них ми поговоримо в наступному уроці.
Правило: Намагайтеся скоротити до мінімуму використання магічних чисел в ваших програмах.