Вказівники

    В інформатиці існує цілий ряд задач, у яких використовуються динамічні структури даних. Динамічними структурами даних вважаються такі, розмір яких заздалегідь невідомий і (чи) змінюється в процесі виконання програми, чи розмір структури перевищує 64 Кбайт. Зв'язані структури є динамічними до них відносяться: списки, стеки, черги, графи, дерева..
    Динамічне розміщення даних передбачає використання динамічної пам'яті. Динамічна пам'ять (куча) - це оперативна пам'ять комп'ютера, надана програмі при її роботі за винятком сегмента даних, стека, пам'яті використовуваної системними і резидентними програмами, і власне тілом самої програми, що виконується. Розмір динамічної пам'яті можна встановлювати з середовища чи із самої програми спеціальними директивами компілятора.
    Кучі спочатку завжди вільна і заповнюється від нижніх адрес в області кучі. За станом кучі можна стежити за допомогою спеціально визначених у мові Pascal перемінних типу вказівник (Pointer):
 HeapOrg - містить у собі адреса початку кучі, її значення не змінюється в процесі виконання програми;
 HeapPtr - містить у собі початок ще невикористаної безперервної ділянки кучі. Кожен раз, коли в кучі розміщається нова змінна, значення цього вказівника змінюється на розмір цієї перемінної;
 FreePtr - указує на кінець кучі (список вільних блоків кучі).


Усі величини типу вказівник містять адреси початку областей пам'яті, у яких розміщаються інші величини.

    Усі вказівники мають однаковий розмір - 4 байт. Значення вказівників представляються у вигляді двох величин типу Word ( сегмент : зсув) і розташовуються в статичній пам'яті. Їх не можна ввести з клавіатури і вивести на пристрої виведення. Для значень вказівників визначена константа Nil. Її значення 0000:0000. Існує два види вказівників: типізовані і нетипізовані. Вони сумісні між собою, але ті величини на які вони вказують несумісні.

Для роботи з вказівниками необхідно:

  1. Описати вказівник
  2. Задати значення вказівнику
  3. Операція разіменування
  4. Звільнити динамічну пам'ять

  • Описати вказівник.

При цьому в статичній області пам'яті буде виділено 4 байт і зв'язаний з його ідентифікатором.


Pascal
    Його можна привласнити чи використовувати спеціальні процедури і функції.
    Функція Addr(X) і операція @X, повертають значення типу Pointer, що є початком області пам'яті, у якій значення перемінної Х. Перемінна Х - може бути будь-якого типу.
    Для виділення динамічної пам'яті величинам за допомогою типізованих вказівників використовується процедура New(TypePtr) , де TypePtr - типізований вказівник. Під дією даної процедури TypePtr - перемінній привласнюється значення визначеної HeapPtr-перемінний, у динамічній пам'яті виділяється місце відповідно типу вказівника і значення перемінної HeapPtr змінюється на величину цього типу. Надалі при допомоги цього вказівника в динамічній пам'яті можна розміщати величини, тип яких відповідає типу типізованого вказівника і мати до них доступ.
Для виділення динамічної пам'яті величинам за допомогою нетипізованих вказівників використовується процедура GetMem(TPtr,Size), де TPtr - нетипізований вказівник типу Pointer, Size - величина типу Word, кількість виділюваної динамічної пам'яті. Під дією даної процедури перемінної TPtr привласнюється значення визначеної перемінної HeapPtr, у динамічній пам'яті виділяється ділянка розміром Size байт, значення перемінної HeapPtr змінюється на величину параметра Size. Надалі, за допомогою цього вказівника, у виділеній ділянці динамічної пам'яті можна розміщати будь-які величини розміром Size байт і мати до них доступ.
Для визначення кількості пам'яті необхідної для розміщення величини будь-якого типу, у мові Pascal визначена функція SizeOf(X) . Де X - ідентифікатор перемінної, функції чи типу. Як свій результат, функція повертає величину типу Word - кількість пам'яті в байтах необхідної для розміщення X.

Суть її складається в переході від вказівника до значення, на яке він указує. Ця операція позначається - ідентифікатор вказівника. Результатом операції є значення величини, на яку вказує вказівник, тобто здійснюється доступ до тієї області пам'яті, з яким зв'язаний вказівник. Разіменованому типізованому вказівнику можна привласнювати значення того типу, яким він описано чи привласнювати його значення перемінним того ж типу. Аналогічно і для нетипізованих вказівників, але тільки треба стежити, щоб розмір пам'яті зазначеної при створенні нетипізованого вказівника, збігався з розміром величини, що ми заносимо в пам'ять, чи в який ми зчитуємо значення. ( приклад)


Pascal
    По закінченню роботи з величинами, розміщеними в динамічній пам'яті, її треба звільнити від них.
    Для типізованих вказівників визначена процедура Dispose(Ідентифікатор вказівника) . Після виконання цієї процедури значення типізованого вказівника невизначене і губиться значення, на яке він указував. Подальше використання цього вказівника можливо, тільки після присвоювання йому значення іншого чи вказівника повторного застосування процедури New. Процедуру Dispose можна застосовувати тільки до типізованих вказівників значення, яких визначено.
Для нетипізованих вказівників визначена процедура FreeMem(P, Size) , де указатель типа величина типа - количество освождаемой динамической памяти. де P вказівник типу Pointer, Size величина типу Word - кількість звільненої динамічної пам'яті. Під дією даної процедури звільняється ділянка пам'яті, починаючи з адреси що знаходиться у вказівнику P, розміром Size. Значення вказівника стає невизначеним. Дану процедуру не можна застосовувати до вказівників значення, яких невизначено і слід уважно стежити за розміром пам'яті, що звільняється. Застосування цих процедур не змінює значення перемінної HeapPtr, а динамічна пам'ять, що звільняється, не враховується як невикористана. На неї не будуть указувати вказівники при наступних викликах процедур: New чи GetMem.
    Для звільнення великих ділянок динамічної пам'яті визначена процедура Release(Ідентифікатор вказівника) . Под действием данной процедуры переменной присваивается значение, используемого в этой процедуре указателя, а участок динамической памяти от значения указателя стоящего в процедуре, до предшествующего значения переменной возвращается в неиспользованную динамическую память. Все указатели связанные с освобождаемым участком динамической памяти, теряют свои значения. Выполнениеосвободит всю динамическую память. Данную процедуру следует применять в сочетании с процедурой Під дією даної процедури перемінної HeapPtr привласнюється значення, вказівника що використовується в цій процедурі , а ділянка динамічної пам'яті, від значення вказівника що стоїть в процедурі, до попереднього значення перемінної HeapPtr, повертається в невикористану динамічну пам'ять. Усі вказівники зв'язані з ділянкою динамічної пам'яті, що звільняється, втрачають свої значення. Виконання Release(HeapOrg) звільнить усю динамічну пам'ять. Дану процедуру варто застосовувати в сполученні з процедурою Mark(Ідентифікатор вказівника) . Під дією даної процедури вказівнику, що стоїть в цієї процедурі, буде привласнене поточне значення перемінної HeapPtr. Цю процедуру потрібно застосовувати до застосування New чи GetMem. А при звільненні динамічної пам'яті, її вказівник використовувати в процедурі Release.
    Для того, щоб стежити за використанням динамічної пам'яті визначені наступні функції. Функція MaxAvail , тип результату LongInt. Дана функція повертає розмір у байтах самого великої вільної ділянки динамічної пам'яті. Він знаходиться як максимальне значення з розміру невикористаної частини кучі і розмірів звільнених до даному моменту ділянок. Функція MemAvail, тип результату LongInt. Дана функція повертає сумарний розмір у байтах усіх вільних ділянок динамічної пам'яті: і звільнених, і ще не використаних.

[На титульну сторінку | Зміст | Далі ]