Засоби створення та використання динамічних даних

Процес створення динамічної змінної складається з наступних кроків:

– пошук та резервування місця у Heap-області для розміщення динамічної змінної;

– засилання адреси зарезервованої ділянки у вказівник;

– заповнення динамічної змінної, тобто занесення значення за адресою у вказівник.

 

Зв’язок вказівника з об’єктом схематично має вигляд:

 

Вказівник може бути порожнім (мати порожню вказівку Nil).

Nil – службове слово.

Після введення у розділі опису вказівника p, він не посилається ні на який програмний об’єкт . Для породження динамічного об’єкту використовується стандартна процедура:

New (Var p:pointer);

де

p – фактичний параметр, вказівник.

 

У результаті виконання оператору процедури породжується новий динамічний об’єкт типу, на який посилається вказівник, що є фактичним параметром процедури New. Вказівнику присвоюється його адреса.

У наведеному прикладі породжується динамічна змінна типу integer, а вказівнику р присвоюється її адреса. При цьому породженому динамічному об’єкту не присвоюється яке-небудь значення, а тільки резервується пам’ять.

Динамічним об’єктам на відміну від статичних імен не дається. Для звернення до динамічного об’єкту використовують змінну – вказівник у вигляді:

p^

Наявність символу ^ після змінної - вказівника говорить про те, що мова йде не про значення самого вказівника, а про значення тієї змінної, на яку вказує вказівник.

Динамічну змінну можна ініціювати будь-яким з відомих засобів.

p^:=10;

– означає присвоєння значення 10 динамічній змінній, на яку вказує p.

Змінна з символом ^ може бути використана у будь-яких конструкціях мови, де припустиме використання змінних того ж типу, що й тип динамічної змінної:

r^:=p^+3;

 

Один з поширених засобів роботи з Неар-областю реалізується за допомогою пари процедур New - Dispose.

Приклад:

 

Var

x1,x2,s : ^integer;

Begin

New(x1);

New(x2);

New(s);

Readln(x1^);

Readln(x2^);

s^:=x1^+x2^;

Writeln(‘сума=’, s^);

Dispose(x1);

Dispose(x2);

Dispose(s)

End.

 

В описі

Var

x1,x2,s : ^integer;

знак ^ перед типом вказує на те, що кожна із змінних x1,x2,s є вказівником на динамічну змінну цілого типу.

У результаті виконання процедур

New(x1);

New(x2);

New(s);

 

у Heap-областi резервується пам’ять для збереження трьох змінних цілого типу, адреси цих областей пам’яті заносяться відповідно у змінні-вказівники x1, x2, s.

Змінна-вказівник, записана зі знаком ^ справа вказує на те, що мова йде не про адресу динамічної змінної, а про значення динамічної змінної. Тому процедури:

Readln (x1^);

Readln (x2^);

здійснюють введення значень самих динамічних змінних, на які посилаються вказівники x1, x2.

Оператором присвоєння

s^:=x1^+x2^;

за адресою s заноситься відповідна сума – так формується третя динамічна змінна, значення якої виводиться на екран:

Writeln (‘сума=’, s^);

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

 

Над вказівниками визначені операції присвоєння, порівняння. Вказівник може мати порожню зсилку: p:=Nil.

 

Наприклад p,d - вказівники:

Var p,d : ^integer;

тоді операції:

p^:=3;

d^:=58;

p :=d;

d :=Nil;

 

схематично можна зобразити у наступному вигляді:

p^:=3; d^:=58;
p :=d;
d :=Nil;

 

Інший засіб розміщення динамічних змінних у Неар-областi реалізується за допомогою двох взаємно пов’язаних процедур:

GetMem (Var P:pointer; Size:work);

FreeMem (Var P:pointer; Size:work);

 

GetMem створює динамічну змінну P^ визначеного розміру Size.

FreeMem – звільнює динамічну змінну заданого розміру.

де р – покажчик (типізований або нетипізований);

Size – константа цілого типу, що задає розмір пам'яті (у байтах). Максимальне значення Size - 64Кб.

 

Виклики GetMem та FreeMem повинні відповідати один одному, а значення для одної пари викликів Size співпадати. Не можна поєднувати GetMem і Dispose чи New і FreeMem.

Дія пар процедур однакова. Однак процедури GetMem, FreeMem мають більш широкий спектр застосування. Наприклад, можна виділити пам’ять визначеного розміру:

GetMem(P,20);

.............

FreeMem(P,20);

– що неможливо у New-процедурі.

 

 

Над динамічними змінними можна виконувати такі ж операції, як і над статичними, наприклад:

r^:=t^+20;

If r^<30 Then t^:=40;

Вказівники, в свою чергу, можна тільки присвоювати один одному та порівнювати на рівність чи нерівність між собою, наприклад:

t:=r;

If t<>r Then t:=Nil;

Вказівникам можна присвоїти константу NIL - порожній, або адресу об'єкта за допомогою функції ADDR або оператора @. Наприклад:

p3 := ADDR(x);

p4 := @x;

Динамічною змінною може бути також рядок або масив, наприклад:

Type

Strichka=String[50];

Mas=Array[1..40] of Real;

Var

VkazStr:^Strichka;

VkazMas:^Mas;

Якщо треба виділити пам'ять під масив достатньо виконати оператор:

New(VkazMas)

Тепер в циклі можна ввести значення його елементів:

For i:=l to 40 do Read(VkazMas^[i]);

Якщо в процесі виконання програми яка-небудь змінна стане непотрібною, її можна вилучити з пам'яті за допомогою оператора Dispose:

Dispose(VkazMas);

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

 

 

Третя пара процедур Mark і Release використовується для фіксації та відновлення стану Неар-області.

Процедура Mark (Var p:pointer) записує поточний стан Heap-області у змінну-вказівник p.

Процедура Release (Var p:pointer) повертає Heap через p до того стану, який був збережений відповідним викликом Mark, тобто за допомогою Release звільняються усі динамічні змінні, розподілені будь-яким чином після виклику Mark.

Функція MemAvail (Var i:longint) повертає розмір вільної пам’яті.

Функція MaxAvail (Var i:longint)– розмір найбільшої безперервної частини вільної пам’яті.

Приклад:

Uses Crt;

Var

i : longint;

p : ^integer;

Begin

ClrScr;

i:=MemAvail;

Writeln(‘.....’, i);

New(p);

p^:=4566;

i:=MemAvail;

Writeln(‘....’, i);

Readln

End.

Протокол роботи:

тобто змінна цілого типу займає у Неар-області 8 байт.

При використанні процедури New з’являється серйозна проблема: можливе вичерпання області пам’яті, відведеної під динамічні змiннi. Якщо при виконанні New з’ясовується, що для розміщення нової змінної в кучі не вистачає пам’яті, то значення вказівника, заданого у параметрі процедури, не зміниться. При цьому виконання програми не перерветься i ніяких повідомлень видано не буде. Тому подальша робота з невстановленим вказівником може привести до некоректної роботи програми.

Для підвищення надійності програми потрібно перевіряти поточний стан динамічної пам’яті перед кожним зверненням до New. Це можна зробити за допомогою стандартної функції MaxAvail, яка повертає максимальний розмір безперервної частини вільної пам’яті, де можна розмістити динамічну змінну.