C++

Символи та таблиця ASCII

Як тип char насправді зберігає числа, що таке кодування, детальна структура таблиці ASCII, арифметика символів та функції <cctype> — фундамент для розуміння рядків у C++.

Символи та таблиця ASCII

Чому 'A' + 1 дорівнює 'B'

Ви вже знаєте, що в C++ існує тип char для зберігання одного символу. Але подивіться на цей фрагмент коду:

CharMath.cpp
#include <iostream>

using namespace std;

int main()
{
    char first = 'A';
    char next  = first + 1;
    char digit = '7' - '0';

    cout << next  << "\n"; // B
    cout << (int)digit << "\n"; // 7

    return 0;
}

Чому додавання одиниці до символу 'A' дає символ 'B'? Чому віднімання '0' від символу-цифри '7' дає звичайне число 7? І чому взагалі можна виконувати арифметику над символами? Це не магія — це пряме наслідування того, що char насправді є числом. А яке число відповідає якому символу — визначає таблиця кодування.


Тип char — це просто маленький цілочисельний тип

Байт, біти та діапазон значень

Ви вже знаєте зі статті про типи даних, що char займає рівно 1 байт — 8 біт. Вісім біт дають 256 можливих комбінацій: від 0000 0000 до 1111 1111. Отже, char може зберігати одне з 256 різних чисел.

Залежно від того, як компілятор інтерпретує 8-й біт (знаковий чи ні), розрізняють:

  • signed char — діапазон від −128 до 127 (знаковий тип, старший біт = знак)
  • unsigned char — діапазон від 0 до 255 (беззнаковий тип)
  • char — залежить від платформи; на більшості сучасних систем signed, але це не гарантовано стандартом
CharTypes.cpp
#include <iostream>

using namespace std;

int main()
{
    signed char   sc = -1;
    unsigned char uc = 255;
    char          ch = 'A';  // 65 у внутрішньому представленні

    cout << (int)sc << "\n"; // -1
    cout << (int)uc << "\n"; // 255
    cout << (int)ch << "\n"; // 65

    return 0;
}

Зверніть увагу на (int)ch — без явного приведення std::cout виведе символ A, а не число 65. Ми говоримо компілятору: «інтерпретуй цей байт як ціле число, а не символ».

Тип char є окремим від signed char і unsigned char навіть попри те, що фізично відповідає одному з них. Стандарт C++ трактує char, signed char та unsigned char як три різних типи. Це важливо при перевантаженні функцій та шаблонах.

char у пам'яті

Символ 'A' зберігається в пам'яті як байт зі значенням 65 (або 0x41 у шістнадцятковій):

char ch = 'A'
Hex Dump / ASCII
0x000000F0
41000000000000000000000000000000
Offset: 4 bytes
Big Endian

Порівняйте з int x = 65 — значення те саме, але займає 4 байти:

int x = 65
Hex Dump / ASCII
0x000000F4
41000000000000000000000000000000
Offset: 4 bytes
Big Endian

char ch = 'A' і char ch = 65 — це абсолютно ідентичні оголошення. Компілятор зберігає в обох випадках один байт зі значенням 65. Різниця лише в тому, як програміст записує ініціалізатор — одинарні лапки ('A') або числовий літерал (65).

Loading diagram...
@startuml
skinparam style plain
skinparam defaultFontName "JetBrains Mono"
skinparam backgroundColor #f8fafc
skinparam defaultFontSize 13

title signed char vs unsigned char — діапазони значень

rectangle "signed char" as sc #3b82f6 {
  rectangle "-128" as n1 #1d4ed8
  rectangle "   ...   " as n2 #2563eb
  rectangle "   -1   " as nm1 #2563eb
  rectangle "    0    " as n0 #2563eb
  rectangle "    1    " as np1 #2563eb
  rectangle "   ...   " as n3 #2563eb
  rectangle "+127" as n5 #1d4ed8
}

rectangle "unsigned char" as uc #22c55e {
  rectangle "  0  " as u1 #16a34a
  rectangle "  1  " as u2 #16a34a
  rectangle " ... " as u3 #16a34a
  rectangle " 128 " as u4 #16a34a
  rectangle " ... " as u5 #16a34a
  rectangle " 255 " as u6 #16a34a
}

note bottom of sc
  char ch = 65 → 'A'
  Знаковий: від'ємні значення можливі.
  Більшість платформ: signed за замовчуванням
end note

note bottom of uc
  unsigned char byte = 200 → коректно!
  Для <cctype>: завжди cast до unsigned char
end note

@enduml

Що таке кодування символів

Проблема: число є, але що воно означає?

Отже, char — це число від 0 до 255. Але саме по собі число нічого не каже. Байт зі значенням 65 — це буква 'A'? Кирилична 'А'? Якийсь спеціальний символ? Відповідь залежить від того, яку таблицю відповідності ми використовуємо.

Кодування символів (character encoding) — це стандартизована таблиця, що визначає, яке число відповідає якому символу. Це договір між усіма учасниками: комп'ютером, операційною системою, компілятором, текстовим редактором та людиною.

Аналогія з реального світу: уявіть азбуку Морзе. Три крапки (...) — це буква S. Але якщо хтось не знає, що ми використовуємо саме азбуку Морзе, а не, скажімо, шифр Цезаря — три крапки для нього лишаться беззмістовним сигналом. Кодування і є таким «ключем до розшифровки».

Принципово важливо розуміти різницю між значенням і поданням: значення 65 існує незалежно від кодування. Кодування лише визначає, який символ ми намалюємо на екрані, коли зустрінемо байт 65. Саме тому «змінити кодування» файлу — це не змінити байти, а лише змінити правило їх інтерпретації.

Чому потрібен єдиний стандарт

На зорі комп'ютерної ери кожен виробник мав власну таблицю кодування. IBM використовувала EBCDIC, різні термінали — власні схеми. Це означало, що текстовий файл, створений на одній системі, перетворювався на «кракозябри» (mojibake) на іншій. Обмін даними між різними комп'ютерами був вкрай складним завданням.

Рішення прийшло у 1963 році у вигляді стандарту ASCII.


Таблиця ASCII

Що таке ASCII

ASCII (American Standard Code for Information Interchange — Американський стандартний код для обміну інформацією) — перший широко прийнятий стандарт кодування символів, розроблений у США у 1963 році і затверджений як стандарт ANSI у 1968 році.

Ключові характеристики ASCII:

  • Використовує 7 біт → 128 символів (коди від 0 до 127)
  • Розроблявся для телетайпів (teletypes) — пристроїв передачі тексту по телефонних лініях
  • Орієнтований на англійську мову: латинські букви, цифри, пунктуація
  • Досі є основою всіх сучасних кодувань — UTF-8 повністю сумісний з ASCII
Чому 7 біт, а не 8? У 1963 році 8-й біт часто використовувався для контролю парності (parity bit) — примітивної перевірки цілісності даних при передачі. Тому для символів залишили 7 біт. Восьмий біт пізніше дав простір для «розширеного ASCII» з 128 додатковими символами — про це розповімо наприкінці статті.

Структура таблиці ASCII

Loading diagram...
@startuml
skinparam style plain
skinparam defaultFontName "JetBrains Mono"
skinparam backgroundColor #f8fafc
skinparam defaultFontSize 13

title Таблиця ASCII — 4 логічні зони (0–127)

rectangle "0–31: Керуючі символи (Control Characters)" as ctrl #64748b {
  rectangle "0  → \\0  NUL (нуль-термінатор)" as nul #475569
  rectangle "7  → \\a  BEL (дзвінок)" as bel #475569
  rectangle "9  → \\t  TAB (табуляція)" as tab #475569
  rectangle "10 → \\n  LF  (новий рядок)" as lf  #475569
  rectangle "13 → \\r  CR  (повернення каретки)" as cr  #475569
  rectangle "27 → ESC" as esc #334155
}

rectangle "32–47 та 58–127: Пробіл і пунктуація" as punct #475569 {
  rectangle "32: SPACE" as sp  #334155
  rectangle "33: !" as ex  #334155
  rectangle "46: ." as dot #334155
  rectangle "64: @" as at  #334155
}

rectangle "48–57: Цифри '0'–'9'" as digits #3b82f6 {
  rectangle "48: '0'" as d0 #2563eb
  rectangle "49: '1'" as d1 #2563eb
  rectangle "  ...  " as dd #2563eb
  rectangle "57: '9'" as d9 #1d4ed8
}

rectangle "65–90: Великі літери 'A'–'Z'" as upper #f59e0b {
  rectangle "65: 'A'" as ua #d97706
  rectangle "  ...  " as ud #d97706
  rectangle "90: 'Z'" as uz #d97706
}

rectangle "97–122: Малі літери 'a'–'z'" as lower #22c55e {
  rectangle "97:  'a'" as la #16a34a
  rectangle "  ...  " as ld #16a34a
  rectangle "122: 'z'" as lz #16a34a
}

note right of digits
  '0'=48, тому:
  ch - '0' = числове значення цифри
end note

note right of upper
  'a' - 'A' = 32
  Різниця між регістрами — константа!
end note

note right of lower
  'z' - 'a' = 25
  Всього 26 літер в алфавіті
end note

@enduml

Таблиця ASCII поділяється на чотири логічні групи:

Керуючі символи (0–31)

Невидимі символи для керування пристроями та форматування тексту. Жодного «малюнку» вони не мають — це команди.

Найважливіші:

  • 0 (\0) — нуль-термінатор, кінець C-style рядка
  • 7 (\a) — дзвінок (bell) — звуковий сигнал
  • 8 (\b) — backspace — крок назад
  • 9 (\t) — горизонтальна табуляція
  • 10 (\n) — новий рядок (LF, Line Feed)
  • 13 (\r) — повернення каретки (CR, Carriage Return)
  • 27 — escape-символ (ESC)

Пунктуація і спецсимволи (32–47, 58–64, 91–96, 123–127)

Пробіл (32), !, ", #, $, %, &, ', (, ), *, +, ,, -, ., / та інша пунктуація. Також :, ;, <, =, >, ?, @, дужки, \, ^, _, `, {, |, }, ~, DEL (127).

Цифри (48–57)

Символи '0' через '9'. Зверніть увагу: символ '0' має код 48, а не 0. Саме тому '7' - '0' = 55 - 48 = 7.

Літери (65–90 та 97–122)

Великі літери 'A''Z': коди 65–90.
Малі літери 'a''z': коди 97–122.
Різниця між великою і малою літерою: рівно 32. Саме тому перетворення регістру — це проста арифметика.

Повна таблиця ASCII (32–127)

DecHexSymDecHexSymDecHexSymDecHexSym
3220 563888050P10468h
3321!573998151Q10569i
3422"583A:8252R1066Aj
3523#593B;8353S1076Bk
3624$603C<8454T1086Cl
3725%613D=8555U1096Dm
3826&623E>8656V1106En
3927'633F?8757W1116Fo
4028(6440@8858X11270p
4129)6541A8959Y11371q
422A*6642B905AZ11472r
432B+6743C915B[11573s
442C,6844D925C\11674t
452D-6945E935D]11775u
462E.7046F945E^11876v
472F/7147G955F_11977w
483007248H9660`12078x
493117349I9761a12179y
50322744AJ9862b1227Az
51333754BK9963c1237B{
52344764CL10064d1247C|
53355774DM10165e1257D}
54366784EN10266f1267E~
55377794FO10367g1277FDEL

Арифметика символів

Оскільки char — це число, з символами можна виконувати усі арифметичні та порівняльні операції. Це не просто цікавий факт — це основа багатьох алгоритмів обробки тексту.

Конвертація символу-цифри в числове значення

Символи '0''9' розташовані в ASCII послідовно (коди 48–57). Тому щоб отримати числове значення з символу-цифри, достатньо відняти код символу '0':

DigitConvert.cpp
#include <iostream>

using namespace std;

int main()
{
    char ch = '7';

    // Неправильно: ch сам по собі — це число 55, а не 7
    // Правильно:
    int digit = ch - '0'; // 55 - 48 = 7

    cout << "Символ: " << ch    << "\n"; // 7
    cout << "Код:    " << (int)ch       << "\n"; // 55
    cout << "Число:  " << digit << "\n"; // 7

    return 0;
}
./DigitConvert
$ ./DigitConvert
Символ: 7
Код: 55
Число: 7
Execution finished with exit code 0.

Цей прийом є повсюдним у реальному коді: конвертація рядка з цифрами у число (ручна реалізація atoi), перевірка коректності введення тощо. Важливо пам'ятати, що він лише для символів '0''9' — якщо ch не є цифрою, результат буде безглуздим.

Навігація по алфавіту

Літери 'A''Z' і 'a''z' також розташовані в ASCII послідовно, тому зміщення по алфавіту — це проста арифметика:

AlphabetNav.cpp
#include <iostream>

using namespace std;

int main()
{
    char letter = 'E';

    // Зсув вперед по алфавіту
    char shifted = letter + 3; // 'E' (69) + 3 = 72 = 'H'
    cout << shifted << "\n"; // H

    // Позиція літери в алфавіті (0-indexed)
    int pos = letter - 'A'; // 69 - 65 = 4
    cout << "Позиція 'E': " << pos << "\n"; // 4

    // Циклічний шифр Цезаря (зсув на 13, ROT13)
    char rotated = 'A' + (letter - 'A' + 13) % 26; // 'R'
    cout << "ROT13('E'): " << rotated << "\n"; // R

    return 0;
}
./AlphabetNav
$ ./AlphabetNav
H
Позиція 'E': 4
ROT13('E'): R
Execution finished with exit code 0.

Перетворення регістру вручну

Оскільки між кодами великих і малих літер різниця рівно 32, перетворення регістру — це додавання або віднімання константи:

CaseConvert.cpp
#include <iostream>

using namespace std;

int main()
{
    char upper = 'G';
    char lower = 'g';

    // Велика → мала: додати 32 (або 'a' - 'A')
    char toLower = upper + ('a' - 'A'); // 71 + 32 = 103 = 'g'

    // Мала → велика: відняти 32
    char toUpper = lower - ('a' - 'A'); // 103 - 32 = 71 = 'G'

    cout << toLower << "\n"; // g
    cout << toUpper << "\n"; // G

    // Перевірка: чи є символ великою літерою
    bool isUpper = (upper >= 'A' && upper <= 'Z');
    cout << boolalpha << isUpper << "\n"; // true

    return 0;
}
Замість магічної константи 32 краще писати 'a' - 'A' — це самодокументований код, який явно показує різницю між регістрами. Компілятор обчислить 'a' - 'A' на етапі компіляції і підставить 32 — жодних витрат часу виконання.

Порівняння символів

Оскільки порівняння символів — це порівняння їх числових кодів, 'a' < 'b' є правдою (97 < 98), а 'A' < 'a' — теж правда (65 < 97). Це є основою лексикографічного (алфавітного) сортування рядків:

CharCompare.cpp
#include <iostream>

using namespace std;

int main()
{
    cout << boolalpha;

    // Порівняння символів
    cout << ('a' < 'b')  << "\n"; // true  (97 < 98)
    cout << ('A' < 'a')  << "\n"; // true  (65 < 97)
    cout << ('Z' < 'a')  << "\n"; // true  (90 < 97) — усі великі < малих
    cout << ('9' < 'A')  << "\n"; // true  (57 < 65) — цифри < букв

    return 0;
}
./CharCompare
$ ./CharCompare
true
true
true
true
Execution finished with exit code 0.

Це має практичний наслідок: функція strcmp для рядків порівнює їх саме побайтово за ASCII-кодами. «Banana» стоятиме після «Apple» лексикографічно, бо 'B' (66) > 'A' (65). Але «apple» стоятиме після «Banana», бо 'a' (97) > 'B' (66). Регістр символів впливає на лексикографічний порядок у чистому ASCII.

Loading diagram...
@startuml
skinparam style plain
skinparam defaultFontName "JetBrains Mono"
skinparam backgroundColor #f8fafc
skinparam defaultFontSize 13

title Арифметика символів — практичні формули

rectangle "Цифра → число" as dig #3b82f6 {
  rectangle "ch = '7'   (ASCII 55)" as ch1 #2563eb
  rectangle "digit = ch - '0'" as op1 #2563eb
  rectangle "55 - 48 = 7" as r1 #1d4ed8
}
ch1 -down-> op1
op1 -down-> r1

rectangle "Велика → мала літера" as u2l #f59e0b {
  rectangle "ch = 'G'   (ASCII 71)" as ch2 #d97706
  rectangle "ch + ('a' - 'A')" as op2 #d97706
  rectangle "71 + 32 = 103 = 'g'" as r2 #b45309
}
ch2 -down-> op2
op2 -down-> r2

rectangle "Зміщення в алфавіті" as alpha #22c55e {
  rectangle "ch = 'A'   (ASCII 65)" as ch3 #16a34a
  rectangle "ch + 3" as op3 #16a34a
  rectangle "65 + 3 = 68 = 'D'" as r3 #15803d
}
ch3 -down-> op3
op3 -down-> r3

rectangle "Позиція літери (0-indexed)" as pos #64748b {
  rectangle "ch = 'E'   (ASCII 69)" as ch4 #475569
  rectangle "ch - 'A'" as op4 #475569
  rectangle "69 - 65 = 4" as r4 #334155
}
ch4 -down-> op4
op4 -down-> r4

note bottom
  Вся арифметика — звичайна цілочисельна!
  char — це просто маленький int
end note

@enduml

Бібліотека <cctype>: стандартні функції для символів

Писати власні перевірки на кшталт ch >= 'A' && ch <= 'Z' — це не лише багатослівно, але й ненадійно: такий підхід не враховує локалізацію і може давати неправильні результати для розширеного ASCII. Стандартна бібліотека C++ пропонує готові функції у заголовку <cctype>.

Функції класифікації символів

Усі функції приймають int (неявне розширення char) і повертають ненульове значення (true) або нуль (false):

ClassifyChar.cpp
#include <iostream>
#include <cctype>

using namespace std;

int main()
{
    char samples[] = {'A', 'z', '5', ' ', '!', '\n'};

    for (char ch : samples)
    {
        cout << "'" << ch << "' (код " << (int)ch << "):\n";
        cout << "  isalpha:  " << boolalpha << (bool)isalpha(ch) << "\n";
        cout << "  isdigit:  " << (bool)isdigit(ch) << "\n";
        cout << "  isalnum:  " << (bool)isalnum(ch) << "\n";
        cout << "  isspace:  " << (bool)isspace(ch) << "\n";
        cout << "  ispunct:  " << (bool)ispunct(ch) << "\n";
        cout << "  isupper:  " << (bool)isupper(ch) << "\n";
        cout << "  islower:  " << (bool)islower(ch) << "\n";
    }

    return 0;
}

Таблиця функцій <cctype>

isalpha(ch)
bool
Чи є символ літерою (A–Z або a–z). isalpha('A') → true, isalpha('5') → false.
isdigit(ch)
bool
Чи є символ десятковою цифрою (0–9). isdigit('7') → true, isdigit('a') → false.
isalnum(ch)
bool
Чи є символ літерою або цифрою. Об'єднання isalpha і isdigit.
isspace(ch)
bool
Чи є символ пробільним: пробіл (32), \t (9), \n (10), \r (13), \f (12), \v (11).
ispunct(ch)
bool
Чи є символ розділовим знаком: !, ", #, $, %, &, ', (, ), *, +, ,, -, ., /, :, ;, <, =, >, ?, @, [, \, ], ^, _, `, {, |, }, ~.
isupper(ch) / islower(ch)
bool
Чи є символ великою / малою літерою.
isprint(ch)
bool
Чи є символ друкованим (коди 32–126 включно, тобто все крім керуючих символів та DEL).
iscntrl(ch)
bool
Чи є символ керуючим (коди 0–31 та 127).
toupper(ch)
int
Перетворює символ на велику літеру, якщо він є малою. Інакше повертає символ без змін. Повертає int — не забувайте про static_cast<char>.
tolower(ch)
int
Перетворює символ на малу літеру. Повертає int.

Практичний приклад: валідація та нормалізація введення

Normalize.cpp
#include <iostream>
#include <cctype>
#include <string>

using namespace std;

// Перевіряє, чи рядок є коректним ідентифікатором C++:
// починається з літери або _, далі — букви, цифри або _
bool isValidIdentifier(const string& s)
{
    if (s.empty()) return false;
    if (!isalpha(static_cast<unsigned char>(s[0])) && s[0] != '_')
        return false;

    for (size_t i = 1; i < s.size(); ++i)
    {
        if (!isalnum(static_cast<unsigned char>(s[i])) && s[i] != '_')
            return false;
    }
    return true;
}

// Перетворює рядок у верхній регістр
string toUpperCase(string s)
{
    for (char& ch : s)
        ch = static_cast<char>(toupper(static_cast<unsigned char>(ch)));
    return s;
}

int main()
{
    cout << boolalpha;
    cout << isValidIdentifier("myVar")   << "\n"; // true
    cout << isValidIdentifier("_count")  << "\n"; // true
    cout << isValidIdentifier("2fast")   << "\n"; // false
    cout << isValidIdentifier("my-var")  << "\n"; // false

    cout << toUpperCase("Hello, World!") << "\n"; // HELLO, WORLD!

    return 0;
}
./Normalize
$ ./Normalize
true
true
false
false
HELLO, WORLD!
Execution finished with exit code 0.
Функції <cctype> приймають int, а не char. Якщо ваша платформа використовує signed char і символ має від'ємне значення (наприклад, кирилиця в CP1251), передача такого char напряму є невизначеною поведінкою (UB). Правильний спосіб: std::isupper(static_cast<unsigned char>(ch)).

Loading diagram...
@startuml
skinparam style plain
skinparam defaultFontName "JetBrains Mono"
skinparam backgroundColor #f8fafc
skinparam defaultFontSize 13

title Проблема кодових сторінок — байт 0xC0 у різних кодуваннях

rectangle "Байт у файлі" as byte #334155 {
  rectangle "0xC0  (11000000 у двійковому)" as bits #475569
}

rectangle "CP1251 (Windows-кирилиця)" as cp1251 #3b82f6 {
  rectangle "0xC0 → 'А' (кирилична велика А)" as r1251 #2563eb
}

rectangle "ISO 8859-1 (Latin-1, Захід Європи)" as iso #f59e0b {
  rectangle "0xC0 → 'À' (латинська A з наголосом)" as riso #d97706
}

rectangle "CP437 (DOS псевдографіка)" as cp437 #22c55e {
  rectangle "0xC0 → '└' (кут таблиці)" as r437 #16a34a
}

rectangle "KOI8-U (Unix-кирилиця)" as koi #64748b {
  rectangle "0xC0 → 'ю' (кирилична мала ю)" as rkoi #475569
}

byte -right-> cp1251 : "Windows"
byte -down->  iso    : "Unix/HTTP"
byte -left->  cp437  : "DOS"
byte -up->    koi    : "Unix email"

note bottom
  Один і той самий байт —
  чотири різні символи!
  Результат: «кракозябри» (mojibake)
  Рішення → Unicode (наступна стаття)
end note

@enduml

Extended ASCII та проблема кодових сторінок

8-й біт: ще 128 символів

Оригінальний ASCII використовує лише 7 біт (коди 0–127). Восьмий біт залишався вільним, і різні компанії та організації почали заповнювати коди 128–255 власними символами. Це і є розширений ASCII (Extended ASCII).

Проблема в тому, що єдиного стандарту розширеного ASCII не існує. Кожна «кодова сторінка» (code page) по-своєму визначає, що означають коди 128–255:

Кодова сторінкаПризначенняДе використовується
CP437Псевдографіка DOS (, , , )Старі DOS-програми
CP1251 (Windows-1251)Кирилиця (українська, російська)Windows-системи
CP1252 (Windows-1252)Западноєвропейські мовиWindows, Захід Європи
ISO 8859-1 (Latin-1)Западноєвропейські мовиUnix-системи, HTTP
ISO 8859-5КирилицяUnix-системи
KOI8-UКирилиця (українська)Старі Unix/email системи

«Кракозябри» — наочна демонстрація проблеми

Уявіть: ви зберегли текст "Привіт" у файлі з кодуванням CP1251. Байти виглядають так:

0xCF 0xF0 0xE8 0xE2 0xB3 0xF2  (CP1251: "Привіт")

Якщо відкрити той самий файл, але вказати кодування ISO 8859-1 (Latin-1), браузер або редактор інтерпретує байт 0xCF як Ï, 0xF0 як ð, 0xE8 як è — і замість «Привіт» ви побачите: Ïðèâ³ò. Це і є «кракозябри» (mojibake).

Код C++, що жорстко покладається на розширений ASCII (наприклад, порівнює байти кирилиці з числовими константами), буде непортабельним: він може давати різні результати на Windows (CP1251) та Linux (UTF-8). Сучасне вирішення — стандарт Unicode, якому присвячена наступна стаття.

Тизер: чому ASCII більше недостатньо

ASCII відмінно вирішив задачу для англійської мови. Але наш світ — багатомовний. Китайська мова має тисячі ієрогліфів, арабська — пишеться справа наліво, математична нотація вимагає грецьких та спеціальних літер, емодзі стали частиною цифрового спілкування.

Жодна кодова сторінка з 256 символами не здатна охопити всю людську писемність. У 1991 році світ отримав Unicode — єдиний каталог для понад мільйона символів усіх мов і скриптів. Але це — тема наступної статті.


Практика

Рівень 1: Розуміння та базові операції

Рівень 2: Алгоритми обробки символів

Рівень 3: Більш складні завдання


Резюме

🔢 char — це число

  • char займає 1 байт і може зберігати значення 0–255
  • char ch = 'A'char ch = 65 — ідентичні оголошення
  • signed char (−128..127), unsigned char (0..255), char (платформозалежний)
  • Для виводу числового значення: (int)ch або static_cast<int>(ch)

📋 Таблиця ASCII

  • 128 символів (7 біт), коди 0–127
  • 0–31: керуючі символи (\0, \n, \t, \r)
  • 48–57: цифри '0''9'
  • 65–90: великі літери 'A''Z'
  • 97–122: малі літери 'a''z'
  • Різниця великих і малих: 32

➕ Арифметика символів

  • '7' - '0' → числове значення цифри (7)
  • 'a' - 'A' → 32 (різниця регістрів)
  • 'A' + pos → літера за позицією в алфавіті
  • ch >= 'A' && ch <= 'Z' → перевірка на велику літеру
  • 'A' + (ch - 'A' + n) % 26 → циклічний зсув по алфавіту

🔧 Бібліотека <cctype>

  • isalpha, isdigit, isalnum — перевірка типу символу
  • isspace, ispunct, isprint, iscntrl — додаткові категорії
  • isupper, islower — перевірка регістру
  • toupper, tolower — зміна регістру (повертають int!)
  • ⚠️ Завжди: static_cast<unsigned char>(ch) перед викликом

⚠️ Extended ASCII і кодові сторінки

  • Коди 128–255 не стандартизовані
  • Різні кодові сторінки (CP1251, CP1252, ISO 8859-x) — різна інтерпретація
  • «Кракозябри» (mojibake) — наслідок неправильного кодування
  • ASCII стандартизував лише 128 символів для англійської мови

🌍 Що далі: Unicode

  • 256 значень char не вистачає для всіх мов світу
  • Множинні кодові сторінки → несумісність та помилки
  • Unicode (1991): єдиний каталог для всіх символів усіх мов
  • UTF-8, UTF-16, UTF-32 — різні способи кодування Unicode
  • Наступна стаття: детально про Unicode та кодування UTF
Наступний крок — стаття «Unicode та кодування UTF»: дізнаємось, що таке code point, чим відрізняється UTF-8 від UTF-16, чому "Привіт".length() повертає 12, а не 6, і які символьні типи є в C++ для роботи з Unicode.
Copyright © 2026