Введення
Адресація динамічних змінних відбувається через покажчики. У Паскалі можна визначити змінні, які мають тип покажчик, їх значення визначають адресу об'єкта. Для роботи з динамічними змінними в програмі повинні бути передбачені:
виділення пам'яті під динамічну змінну;
присвоєння вказівником на динамічну змінну адреси виділеної пам'яті (ініціалізація покажчика);
звільнення пам'яті після використання динамічної змінної.
1. Покажчики. Опис покажчиків
Покажчики - це особливий тип даних. У змінних цього типу зберігаються адреси інших змінних що містять корисну для програми інформацію. На перший погляд може здатися, що використання покажчиків призводить до зайвих витрат пам'яті і до ускладнення програми, а також істоті ускладнює й сам процес програмування. У даній главі ми, наведемо такі приклади використання покажчиків, з яких стане ясно, що всі додаткові витрати на їх зберігання та обробку окуповуються в повній мірі. Робота з вказівниками передбачена не тільки в Pascal, але і в деяких інших мовах програмування. Наприклад, у мові С покажчики використовуються практично в будь-якій програмі.
В Pascal роль покажчиків дещо скромніше, і, тим не менш, початківцям програмістам слід засвоїти базові принципи роботи з вказівниками, щоб глибше зрозуміти внутрішній механізм обробки і виконання будь-якої комп'ютерної програми.
2. Покажчики та адреси
Відомо, що адресою змінної є адреса першого байта комірки пам'яті, яка під неї відводиться. Для даних структурних типів (масивів і записів) їх адресою вважається адреса першого байта першого елемента.
В Turbo Pascal існує можливість прямого доступу до будь байту оперативної пам'яті за його адресою при допомоги визначених у модулі system масивів Mem, MemW і MemL, які дозволяють записати інформацію або прочитати її безпосередньо з комірок пам'яті (Один, два або чотири байти). Це дуже небезпечні дії, тому вони виключені в 32 - розрядних системах програмування. Все ж дамо короткі пояснення для тих, хто працює в середовищі Borland (Turbo) Pascal.
В якості індексу в цих масивах використовується адреса, записаний у вигляді, прийнятому в DOS: сегмент: Зсув відносно початку сегменту. Такий дивний спосіб запису адреси пов'язаний з тим, що в операційній системі DOS вся пам'ять розбита на сегменти, розміри яких не перевищують 64 Кбайт. Для отримання абсолютної адреси з пари сегмент Зсув система додає до сегменту справа шістнадцятковий нуль (Це чотири нулі в двійковій системі), а потім складає його зі зміщенням. Таким способом можна адресувати 1 Мбайт пам'яті.
Наприклад, початкова адреса відеобуфера запишеться у вигляді $ B800: [fde_1328383079_8093813237_1328383079_8730893312_2573] 0, а звернутися до найпершого його байту можна так:
Mem [$ В800: [fde_1328383079_8093813237_1328383079_2833318079_187] 00],
до перших двох байтах - MemW [$ B800: [fde_1328383079_8093813237_1328383079_8091723338_7644] 00],
до першим чотирьом байтам - MemL [$ B800: [fde_1328383079_8093813237_1328383079_3381329708_4458] 00]
Абсолютний адреса, відповідний даній парі, - $ B8000.
Ще один приклад для допитливих - оператор mem [0: C]: = mem [0: А]; можна застосувати для примусового очищення буфера клавіатури. Тут адресу маркера кінця буфера клавіатури прирівнюється до адресою його початку. Звичайно, в даному випадку краще скористатися засобами модуля crt.
Є ще один спосіб звернення до оперативної пам'яті - використання службового слова absolute при описі змінної. У цьому випадку змінна буде розташовуватися саме по тією адресою в оперативній пам'яті, який вказаний після absolute. Зрозуміло, використання службового слова absolute - настільки ж небезпечний спосіб, як і звернення до пам'яті через зумовлені масиви.
Однак absolute може використовуватися і більш безпечним способом, дозволяючи поєднувати в пам'яті два змінні з різними іменами. У мові Pascal є спеціальна операція отримання покажчика на змінну (або процедуру) - вона позначається як @. Мається також еквівалентна їй функція addr.
Наприклад, @ x або addr (х) - Адресу змінної х.
Є і зворотна операція отримання значення змінної по її адресою, яка позначається знаком ^. Наприклад, р ^ змінна з адресою р.
У повсякденній практиці засоби роботи з адресами використовуються досить рідко. Основне призначення покажчиків полягає в тому, щоб забезпечити механізм використання у програмі динамічних змінних. Цей механізм ми і будемо обговорювати докладно в наступних розділах.
3. Опис покажчиків
У Pascal є два різних види покажчиків: типізовані і нетипізовані. Типізований покажчик - це покажчик на змінну певного типу, наприклад, цілого, строкового або типу масиву Нетіпізарованний покажчик - це адреса першого байта області пам'яті, в якій може розміщуватися будь-яка інформація поза залежно від її типу.
Опис двох видів покажчиків виконується по-різному:
var p1: ^ integer; {Покажчик на змінну цілого типу}
p2: ^ string; {покажчик на стоку}
p3 pointer; {Нетипізований покажчик}
Зауважимо що тип pointer сумісний з усіма типами покажчиків. У подальшому викладі для зручності імена всіх покажчиків будемо починати з букви p (pointer).
Кожен покажчик розміщується в сегменті даних або в стеку (якщо він оголошений в підпрограмі) та займає там 4 байти. Це додаткові "накладні витрати 'пам'яті. Тому звичайні змінні дуже рідко створюють і знищують динамічно, залишаючи цю можливість для великих сукупностей даних.
Чим більше розмір динамічної змінної, тим менше частка накладних витрат. Наприклад, при зберіганні в динамічній пам'яті масивів великих розмірів зайві 4 байти, витрачені на покажчик, несуттєві.
покажчик динамічний пам'ять адресація
4. Оголошення покажчиків
Як правило, в Турбо Паскалі покажчик зв'язується з певним типом даних. Такі покажчики будемо називати типізований. Для оголошення типизированного покажчика використовується значок А, який поміщається перед відповідним типом, наприклад:
var
p1: ^ integer;
р2: ^ real;
type
PerconPointer = ^ PerconRecord;
PerconRecord = record
Name: string;
Job: string;
Next: PerconPointer
end;
Зверніть увагу: при оголошенні типу PerconPointer ми послалися на PerconRecord, який попередньо в програмі оголошений не був. Як уже зазначалося, в Турбо Паскалі послідовно проводиться в життя принцип, відповідно до якого перед використанням якого-небудь ідентифікатора він повинен бути описаний. Виняток зроблено тільки для покажчиків, які можуть посилатися на ще не оголошений тип даних. Цей виняток зроблено не випадково. Динамічна пам'ять дає можливість реалізувати широко використовувану в деяких програмах організацію даних у вигляді списків. Кожен елемент списку має у своєму складі покажчик на сусідній елемент, що забезпечує ливість перегляду і корекції списку. Якщо б в Турбо Паскалі не було цього винятку, реалізація списків була б значно ускладнена.
У Турбо Паскалі можна оголошувати покажчик і не пов'язувати його при цьому з будь-яким конкретним типом даних. Для цього служить стандартний тип POINTER, наприклад:
var
р: pointer;
Покажчики такого роду будемо називати нетипізований. Оскільки нетипізовані покажчика не пов'язані з конкретним типом, з їх допомогою зручно динамічно розміщувати дані, структура і тип яких змінюються в ході роботи програми.
Як вже говорилося, значеннями покажчиків є адреси змінних в пам'яті, тому варто було б очікувати, що значення одного укаателя можна передавати іншому. Насправді це не зовсім так. У Турбо Паскалі мо...