Зміст:
- 1 Урок №33. Розмір типів даних
- 2 Урок №38. Символьний тип даних char
- 2.1 Тип даних char
- 2.2 Вивід символів
- 2.3 Оператор static_cast
- 2.4 Ввід символів
- 2.5 Розмір, діапазон і знак типу сhar
- 2.6 Керуючі символи
- 2.7 Що використовувати: ‘\n’ чи std::endl?
- 2.8 Інші символьні типи: wchar_t, char16_t і char32_t
- 2.9 Різниця між одинарними і подвійними лапками при використанні з символами
Урок №33. Розмір типів даних
Як ми вже знаємо з уроку №31, пам’ять на сучасних комп’ютерах, як правило, організована в блоки, які складаються з байтів, причому кожен блок має свою унікальну адресу. До цього моменту, пам’ять можна було порівнювати з поштовими скриньками (ті, які знаходяться в кожному під’їзді), куди ми можемо помістити інформацію і звідки ми її можемо витягти, а змінні — це всього лише номери цих поштових скриньок.
Проте, ця аналогія не зовсім підходить до програмування, так як змінні можуть займати більше 1 байту пам’яті. Отже, одна змінна може використовувати 2, 4 або навіть 8 послідовних адрес. Об’єм пам’яті, який використовує змінна, залежить від її типу даних. Оскільки ми отримуємо доступ до пам’яті через імена змінних, а не через адреси пам’яті, то компілятор може приховувати від нас всі деталі роботи зі змінними різних розмірів.
Є декілька причин, чому корисно знати, скільки пам’яті займає певна змінна або тип даних.
По-перше, чим більше вона займає, тим більше інформації може зберігати. Оскільки кожен біт може містити або 0 , або 1 , то 1 біт може мати 2 можливих значення.
2 біти можуть мати 4 можливих значення:
біт 0 | біт 1 |
0 | 0 |
0 | 1 |
1 | 0 |
1 | 1 |
3 біти можуть мати 8 можливих значень:
біт 0 | біт 1 | біт 2 |
0 | 0 | 0 |
0 | 0 | 1 |
0 | 1 | 0 |
0 | 1 | 1 |
1 | 0 | 0 |
1 | 0 | 1 |
1 | 1 | 0 |
1 | 1 | 1 |
По суті, змінна з n-ною кількістю біт може мати 2 n можливих значень. Оскільки байт складається з 8 біт, то він може мати 2 8 (256) можливих значень.
Розмір змінної накладає обмеження на кількість інформації, яку вона може зберігати. Отже, змінні, які використовують більше байт, можуть зберігати більш широкий діапазон значень.
По-друге, комп’ютери мають обмежену кількість вільної пам’яті. Кожен раз, коли ми оголошуємо змінну, невелика частина цієї вільної пам’яті виділяється до тих пір, поки змінна існує. Оскільки сучасні комп’ютери мають багато пам’яті, то в більшості випадків це не є проблемою, особливо коли в програмі всього лише декілька змінних. Проте для програм з великою кількістю змінних (наприклад, 100 000 змінних) різниця між використанням 1-байтових або 8-байтових змінних може бути значною.
Розмір основних типів даних в С++
Виникає питання: “Скільки пам’яті займають змінні різних типів даних?”. Ви можете здивуватися, але розмір певних типів даних залежить від компілятора і/або архітектури комп’ютера!
Мова C++ гарантує тільки їх мінімальний розмір:
Тип | Мінімальний розмір | |
Логічний тип даних | bool | 1 байт |
Символьний тип даних | char | 1 байт |
wchar_t | 1 байт | |
char16_t | 2 байти | |
char32_t | 4 байти | |
Цілочисельний тип даних | short | 2 байти |
int | 2 байти | |
long | 4 байти | |
long long | 8 байт | |
Тип даних з плаваючою крапкою | float | 4 байти |
double | 8 байт | |
long double | 8 байт |
Фактичний розмір змінних може відрізнятися на різних комп’ютерах, тому для його визначення використовують оператор sizeof.
Оператор sizeof — це унарний оператор, який обчислює і повертає розмір певної змінної або певного типу даних в байтах. Ви можете скомпілювати і запустити наступну програму, щоб з’ясувати, скільки пам’яті займають різні типи даних на вашому комп’ютері:
Урок №38. Символьний тип даних char
Хоча тип char і відноситься до цілочисельних типів даних (і, таким чином, слідує усім їхнім правилам), робота з char дещо відрізняється, ніж зі звичайними цілочисельними типами.
- Тип даних char
- Вивід символів
- Оператор static_cast
- Ввід символів
- Розмір, діапазон і знак типу сhar
- Керуючі символи
- Що використовувати: ‘\n’ чи std::endl?
- Інші символьні типи: wchar_t, char16_t і char32_t
- Різниця між одинарними і подвійними лапками при використанні з символами
Тип даних char
Змінна типу char займає 1 байт. Однак замість конвертації значення типу char в ціле число, воно інтерпретується як ASCII-символ.
ASCII (від англ. “American Standard Code for Information Interchange”) — це американський стандартний код для обміну інформацією, який визначає спосіб представлення символів англійської мови (+ декілька інших) у вигляді чисел від 0 до 127. Наприклад: код букви ‘а’ — 97, код букви ‘b’ — 98. Символи завжди повинні знаходитися в одинарних лапках.
Таблиця ASCII-символів:
Код | Символ | Код | Символ | Код | Символ | Код | Символ |
0 | NUL (null) | 32 | (space) | 64 | @ | 96 | ` |
1 | SOH (start of header) | 33 | ! | 65 | A | 97 | a |
2 | STX (start of text) | 34 | ” | 66 | B | 98 | b |
3 | ETX (end of text) | 35 | # | 67 | C | 99 | c |
4 | EOT (end of transmission) | 36 | $ | 68 | D | 100 | d |
5 | ENQ (enquiry) | 37 | % | 69 | E | 101 | e |
6 | ACK (acknowledge) | 38 | & | 70 | F | 102 | f |
7 | BEL (bell) | 39 | ’ | 71 | G | 103 | g |
8 | BS (backspace) | 40 | ( | 72 | H | 104 | h |
9 | HT (horizontal tab) | 41 | ) | 73 | I | 105 | i |
10 | LF (line feed/new line) | 42 | * | 74 | J | 106 | j |
11 | VT (vertical tab) | 43 | + | 75 | K | 107 | k |
12 | FF (form feed / new page) | 44 | , | 76 | L | 108 | l |
13 | CR (carriage return) | 45 | – | 77 | M | 109 | m |
14 | SO (shift out) | 46 | . | 78 | N | 110 | n |
15 | SI (shift in) | 47 | / | 79 | O | 111 | o |
16 | DLE (data link escape) | 48 | 0 | 80 | P | 112 | p |
17 | DC1 (data control 1) | 49 | 1 | 81 | Q | 113 | q |
18 | DC2 (data control 2) | 50 | 2 | 82 | R | 114 | r |
19 | DC3 (data control 3) | 51 | 3 | 83 | S | 115 | s |
20 | DC4 (data control 4) | 52 | 4 | 84 | T | 116 | t |
21 | NAK (negative acknowledge) | 53 | 5 | 85 | U | 117 | u |
22 | SYN (synchronous idle) | 54 | 6 | 86 | V | 118 | v |
23 | ETB (end of transmission block) | 55 | 7 | 87 | W | 119 | w |
24 | CAN (cancel) | 56 | 8 | 88 | X | 120 | x |
25 | EM (end of medium) | 57 | 9 | 89 | Y | 121 | y |
26 | SUB (substitute) | 58 | : | 90 | Z | 122 | z |
27 | ESC (escape) | 59 | ; | 91 | [ | 123 | |
28 | FS (file separator) | 60 | 92 | \ | 124 | | | |
29 | GS (group separator) | 61 | = | 93 | ] | 125 | > |
30 | RS (record separator) | 62 | > | 94 | ^ | 126 | ~ |
31 | US (unit separator) | 63 | ? | 95 | _ | 127 | DEL (delete) |
Символи від 0 до 31 в основному використовуються для форматування виводу. Більшість з них вже застаріли.
Символи від 32 до 127 використовуються для виведення. Це літери, цифри, знаки пунктуації, які більшість комп’ютерів використовує для відображення тексту (англійською мовою).
Наступні два стейтменти виконують одне і те ж (присвоюють змінним типу char ціле число 97 ):
char ch1 ( 97 ) ; // ініціалізація змінної типу char цілим числом 97
char ch2 ( ‘a’ ) ; // ініціалізація змінної типу char символом ‘a’ (97)
Будьте уважні при використанні фактичних чисел з числами, які використовуються для представлення символів (з таблиці ASCII). Наступні два стейтменти виконують не одне і те ж:
char ch ( 5 ) ; // ініціалізація змінної типу char цілим числом 5
Вивід символів
При виводі змінних типу char, об’єкт cout виводить символи замість цифр:
char ch ( 97 ) ; // незважаючи на те, що ми ініціалізуємо змінну ch цілим числом
Також ви можете виводити літерали типу char напряму:
Оператор static_cast
Якщо ви хочете вивести символи у вигляді цифр, а не у вигляді букв, то вам потрібно повідомити cout виводити змінні типу char у вигляді цілочисельних значень. Не дуже хороший спосіб це зробити — присвоїти змінній типу int змінну типу char і вивести її:
int i ( ch ) ; // присвоюємо значення змінної ch змінній типу int
std :: cout << i << std :: endl ; // виводимо значення змінної типу int
Кращим способом є конвертація змінної з одного типу даних в інший за допомогою оператора static_cast.
Синтаксис оператора static_cast виглядає наступним чином:
static_cast приймає значення з (вираз) в якості вхідних даних і конвертує його у вказаний нами .
Приклад використання оператора static_cast для конвертації типу char в тип int:
Результат виконання програми:
Запам’ятайте, що static_cast приймає (вираз) в якості вхідних даних. Якщо ми використовуємо змінну в (виразі) , то ця змінна змінює свій тип тільки в стейтменті з оператором static_cast. Процес конвертації ніяк не впливає на вихідну змінну з її значенням! У вищенаведеному прикладі, змінна ch залишається змінною типу char з колишнім значенням, чому є підтвердженням останній стейтмент з cout.
Також в static_cast немає ніякої перевірки діапазону, тому якщо ви спробуєте використати числа, які занадто великі або занадто малі для конвертованого типу, то відбудеться переповнення.
Ввід символів
Наступна програма просить користувача ввести символ. Потім вона виводить цей символ і його ASCII-код:
Результат виконання програми:
Input a keyboard character: q
q has ASCII code 113
Зверніть увагу, навіть якщо cin дозволить вам ввести декілька символів, змінна ch зберігатиме тільки перший символ (саме він і розміщується у змінній). Інша частина користувацького вводу залишиться у вхідному буфері, який використовує cin і буде доступна для використання наступним викликам cin.
Розглянемо це все на практиці:
std :: cout << "Input a keyboard character: " ; // припустимо, що користувач ввів "abcd"
// Звернуть увагу, що наступний cin не просить користувача що-небудь ввести, дані підтягуються з вхідного буферу!
Результат виконання програми:
Input a keyboard character: abcd
a has ASCII code 97
b has ASCII code 98
Розмір, діапазон і знак типу сhar
У мові С++ для змінних типу char завжди виділяється 1 байт. За замовчуванням, char може бути як signed, так і unsigned (хоча зазвичай signed). Якщо ви використовуєте char для зберігання ASCII-символів, то вам не потрібно вказувати знак змінної (так як і signed, і unsigned можуть містити значення від 0 до 127).
Але якщо ви використовуєте тип char для зберігання невеликих цілих чисел, то тоді слід уточнити знак. Змінна типу char signed може зберігати числа від -128 до 127. Змінна типу char unsigned має діапазон від 0 до 255.
Керуючі символи
У мові C++ є керуючі символи (або “escape-послідовності”). Вони починаються з бекслешу ( \ ), а потім іде певна буква чи цифра.
Найбільш поширеним керуючим символом в мові С++ є \n , який позначає символ нового рядка:
First line
Second line
Ще одним часто використовуваним керуючим символом є \t , який замінює клавішу TAB, вставляючи великий відступ:
First part Second part
Таблиця всіх керуючих символів в мові C++:
Назва | Символ | Значення |
Попередження (alert) | \a | Попередження (звуковий сигнал) |
Backspace | \b | Переміщення курсору на одну позицію назад |
formfeed | \f | Переміщення курсору до наступної логічної сторінки |
Символ нового рядка (newline) | \n | Переміщення курсору на наступний рядок |
Повернення каретки (carriage return) | \r | Переміщення курсору на початок рядка |
Горизонтальний таб (horizontal tab) | \t | Вставка горизонтального TAB |
Вертикальний таб (vertical tab) | \v | Вставка вертикального TAB |
Одинарна лапка | \’ | Вставка одинарної лапки (або апострофа) |
Подвійна лапка | \” | Вставка подвійної лапки |
Бекслеш | \\ | Вставка бекслешу |
Знак питання | \? | Вставка знаку питання |
Вісімкове число | \(number) | Конвертація числа з вісімкової системи числення в тип char |
Шістнадцяткове число | \x(number) | Конвертація числа з шістнадцяткової системи числення в тип char |
Розглянемо приклад в коді:
std :: cout << "This string contains a single backslash \\" << std :: endl ;
Результат виконання програми:
“This is quoted text”
This string contains a single backslash \
6F in hex is char ‘o’
Що використовувати: ‘\n’ чи std::endl?
Ви могли помітити, що в останньому прикладі ми використовували \n для переміщення курсору на наступний рядок. Але ми могли б використати і std::endl . Яка між ними різниця? Зараз розберемося.
При використанні std::cout, дані для виводу можуть поміщатися в буфер, тобто std::cout може не відправляти дані відразу на вивід. Замість цього він може залишити їх при собі на деякий час. Це робиться в цілях підвищення продуктивності.
І \n , і std::endl обидва переносять курсор на наступний рядок. Тільки std::endl ще гарантує, що всі дані з буферу будуть виведені, перед тим, як продовжити.
Так коли ж використовувати \n , а коли std::endl ?
Використовуйте std::endl , коли потрібно, щоб ваші дані виводилися відразу ж (наприклад, під час запису в файл або при оновленні індикатора стану будь-якого процесу). Зверніть увагу, це може спричинити за собою незначне зниження продуктивності, особливо якщо запис на пристрій відбувається повільно (наприклад, запис файлу на диск).
Використовуйте \n у всіх інших випадках.
Інші символьні типи: wchar_t, char16_t і char32_t
Тип wchar_t слід уникати в більшості випадків (крім тих, коли відбувається взаємодія з Windows API).
Так само, як стандарт ASCII використовує цілі числа для представлення символів англійської мови, так і інші системи кодування використовують цілі числа для представлення символів інших мов. Найбільш відомий стандарт (після ASCII) — Unicode — має в запасі більше 110 000 цілих чисел для представлення символів різних мов.
Існують наступні кодування Unicode:
UTF-32 — вимагає 32 біти для представлення символу.
UTF-16 — вимагає 16 біт для представлення символу.
UTF-8 — вимагає 8 біт для представлення символу.
Типи char16_t і char32_t були додані в C++11 для підтримки 16-бітних і 32-бітних символів Unicode (8-бітні символи і так підтримуються типом char).
Різниця між одинарними і подвійними лапками при використанні з символами
Як ви вже знаєте, символи завжди поміщаються в одинарні лапки (наприклад, ‘а’ , ‘+’ чи ‘5’ ). Змінна типу char представляє тільки один символ (наприклад, літеру а , символ + чи число 5 ). Щось на кшталт наступного не є коректним:
char ch ( ’56’ ) ; // змінна типу char може містити тільки 1 символ
Текст, який знаходиться в подвійних лапках, називається рядком (наприклад, “Hello, world!” ). Рядок (тип string) — це набір послідовних символів.
Ви можете використовувати літерали типу string в коді: