Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Вказівники та посилання.docx
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
857.42 Кб
Скачать

6.5. Вказівники символів і функції

Рядкова константа, як, наприклад, рядок «Київ» є масивом символів. Компілятор завершує внутрішнє подання такого масиву символом ’\0’ так, що програми можуть знаходити його кінець. Отже, довжина масиву в пам’яті виявляється на одиницю більшою від кількості символів між подвійними лапками (” ”).

Очевидно, найчастіше рядкові константи з’являються як аргументи функцій, як наприклад у printf (”Привіт, Київ\n”). Доступ до символьного рядка в програмі здійснюється за допомогою вказівника символів, тобто функція printf одержує вказівник символьного масиву.

Звичайно, символьні масиви не зобов’язані бути тільки аргументами функцій. Якщо описати змінну message як char *message; то в результаті виконання оператора message = "реальний час"; змінна message стане вказівником на фактичний масив символів. Це не копіювання рядка, оскільки використовуються тільки вказівники. У мові C не передбачені будь-які операції для обробки рядка символів як цілих значень.

Розглянемо функцію Strcpy(s,t), яка копіює рядок t у рядок s. У програмі використаємо масиви (див. ПП6.7 на СD). Ця ж програма, але з використанням вказівників, реалізована як ПП6.8. (див. на СD). Аргументи передаються за значенням, тому функція Strcpy може використати s і t так, як вона побажає. Вказівники пересуваються по масивах (по одному елементу за крок), доти, доки не буде скопійовано в s завершальний в t символ ’\0’.

Інший варіант реалізації функція Strcpy реалізовано як ПП6.9. (див. на СD). Тут збільшення s і t внесено в перевірну частину. Значенням *t++ є символ, на який вказував t до збільшення. Так само цей символ розміщують у стару позицію s, перш ніж s буде збільшено. Кінцевий результат полягає у тому, що всі символи, в тому числі завершальний символ ’\0’, копіюються з t у s. Порівняння з сим­волом ’\0’ зайве, тому функцію можна записати у вигляді ПП6.10 (див. на СD), який має переваги.

Розглянемо функцію Strcmp(s, t) (див. ПП6.11 на СD), яка порівнює символьні рядки s і t, повертає від’ємне, нульове або додатне значення відповідно до того, чи менше, дорівнює або більше лексикографічно s, ніж t. Версію Strcmp із вказівниками реалізовано як ПП6.12 (див. на СD).

6.6. Вказівники – не цілі значення

Вказівник можна присвоїти цілому значенню і передати це значення обернено, не змінивши його. При цьому не відбувається ніякого масштабування або перетворення і жоден біт не губиться. Розглянемо, наприклад, функцію StrSave(s) (див. ПП6.13 на СD), яка копіює рядок s у деяке місце для зберігання, яке виділяється за допомогою функції Alloc, і повертає вказівник на це місце. На практиці є бажання нехтувати описами, як показано в ПП6.14. (див. на СD).

Ця програма буде правильно працювати, оскільки за замовчуванням функції і аргументи мають тип int, а вказівник і ціле значення можна безпечно пересилати туди і назад. Однак такий стиль програмування ризикований, оскільки залежить від деталей реалізації та архітектури ПК і може призвести до неправильних результатів на конкретному компіляторі. Краще всього всюди використати повні описи.

6.7. Багатовимірні масиви

У мові C++ передбачено багатовимірні масиви. Розглянемо завдання перетворення дня місяця в день року і навпаки. Наприклад, перше березня є 60-м днем не високосного року та 61-м днем високосного року. Введемо дві функції для виконання цих перетворень: Day_of_year перетворить місяць і день у точно призначений день року, а Month_day – день року на місяць і день. Остання функція повертає два значення, тому аргументи місяця і дня повинні бути вказівниками:

Month_day(1977, 60, &m, &d)

надає m значення 3 і d значення 1 (1 березня).

Обидві ці функції потребують інформаційну таблицю, яка вказує кількість днів у кожному місяці. Кількості днів у місяці високосного і не високосного років розрізняються, тому простіше подати їх у вигляді двох рядків двовимірного масиву, ніж намагатися простежувати під час обчислень, що саме відбувається в лютому. Такий масив і функції, які виконують ці перетворення, визначено в ПП6.15. (див. на СD). Двовимірний масив day_tab повинен бути зовнішнім для Day_of_year і Month_day, оскільки він використовується обома цими функціями.

За визначенням у мові C++ двовимірний масив є одновимірним масивом кожен елемент якого є масивом. Тому індекси записуються як day_tab[i][j], а не day_tab [i, j], як у більшості мов. Використовувати двовимірні масиви можна так само, як і в інших мовах. Елементи зберігаються по рядках, тобто в разі звернення до елементів у порядку їх розміщення в пам’яті найшвидше змінюється найбільш правий індекс.

Масив ініціалізується за допомогою списку початкових значень, розташованих у фігурних дужках {}, де кожен рядок двовимірного масиву ініціалізується відповідним підсписком. Так у початок масиву day_tab поміщено стовпчик із нулями для того, щоб номери місяців змінювалися від 1 до 12, а не від 0 до 11.

Якщо двовимірний масив передається функції, то опис відповідного аргументу функції повинен містити кількість стовпчик. Кількість рядків несуттєва, оскільки фактично передається вказівник. У розглядуваному випадку це вказівник об’єктів, що є масивами з 13 чисел типу int. Отже, для передачі масиву day_tab функції F опис у F мав би вигляд:

F(day_tab)

int day_tab[2][13];

{...}

Кількість рядків несуттєва, тому опис аргументу в F міг би бути таким: int day_tab[][13]; або int (*day_tab)[13];. Це означає, що аргумент є вказівником масиву з 13 цілих значень. Круглі дужки тут потрібні, оскільки квадратні дужки [] мають вищий рівень старшинства, ніж операція *; Без круглих дужок () int *day_tab[13]; є описом масиву з 13 вказівників на цілі.