Урок №88. Символьні константи рядків C-style

  Юрій  | 

  Оновл. 12 Січ 2021  | 

 30

На попередніх уроках ми вже навчилися створювати і ініціалізувати рядки C-style:

Мова C++ підтримує ще один спосіб створення символьних констант рядків C-style — через вказівники:

Хоча обидві ці програми працюють і показують однакові результати, виділення пам’яті в них виконується по-різному.

У першому випадку в програмі виділяється пам’ять для фіксованого масиву довжиною 5 і ініціалізується ця пам’ять рядком John\0. Оскільки пам’ять була спеціально виділена для масиву, то ми можемо змінювати її вміст. Сам масив розглядається як звичайна локальна змінна, тому, коли він виходить з області видимості, пам’ять, яку він використовував, звільняється для інших об’єктів.

Що відбувається у випадку з символьною константою? Компілятор поміщає рядок John\0 в пам’ять типу read-only (тільки читання), а потім створює вказівник, який вказує на цей рядок. Декілька рядкових літералів з одним і тим же вмістом можуть вказувати на одну і ту ж адресу. Оскільки ця пам’ять доступна тільки для читання, а також тому, що внесення змін до рядкового літералу може вплинути на подальше його використання, найкраще перестрахуватися, оголосивши рядок константним (типу const). Також, оскільки рядки, оголошені таким чином, існують протягом усього “життя” програми (вони мають статичну тривалість, а не автоматичну, як більшість інших локально визначених літералів), нам не потрібно турбуватися про проблеми, пов’язані з областю видимості. Тому наступне в порядку речей:

У фрагменті, наведеному вище, функція getName() повертає вказівник на рядок C-style John. Все добре, так як John не виходить з області видимості, коли getName() завершує своє виконання, тому caller все одно має доступ до рядка.

std::cout і вказівники типу char

На цьому етапі ви, можливо, вже встигли помітити те, як std::cout обробляє вказівники різних типів. Розглянемо наступний приклад:

Результат виконання програми на моєму комп’ютері:

0046FAE8
Hello!
John

Чому в масиві типу int виводиться адреса, а в масивах типу char — рядки?

Справа в тому, що при передачі вказівника НЕ типу char, в результаті виводиться просто вміст цього вказівника (адреса в пам’яті). Однак, якщо ви передасте об’єкт типу char* або const char*, то std::cout припустить, що ви маєте намір вивести рядок на екран. Отже, замість виведення значення вказівника — виведеться рядок, на який він вказує!

Хоча це все чудово в 99% випадків, але це може привести і до несподіваних результатів, наприклад:

Тут ми маємо намір вивести адресу змінної a. Проте, &a має тип char*, тому std::cout виведе це як рядок!

Результат виконання програми на моєму комп’ютері:

R╠╠╠╠╜╡4;¿■A

Чому так? std::cout припустив, що &a (типу char*) є рядком. Тому спочатку вивелося R, а потім операція виведення продовжилася. Наступним в пам’яті було сміття. Зрештою, std::cout зіткнувся з коміркою в пам’яті, яка має значення 0, яке він інтерпретував як нуль-термінатор, і, відповідно, припинив операцію виведення. Те, що ви бачите в результаті, може відрізнятися, в залежності від того, що знаходиться в пам’яті після змінної a.

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

Оцінити статтю:

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (1 оцінок, середня: 5,00 з 5)
Loading...

Залишити відповідь

Ваш E-mail не буде опублікований. Обов'язкові поля відмічені *