- •Динамічний розподіл пам’яті Статичні і динамічні змінні
- •Покажчики Поняття покажчика
- •Розподіл динамічної пам’яті Породження динамічних об'єктів
- •Доступ до динамічних об'єктів
- •Обробка динамічних об'єктів
- •Знищення динамічних об'єктів
- •Методи динамічного розподілу пам’яті
- •Стандартні засоби аналізу стану Heap-області
- •Динамічний розподіл пам’яті
- •Динамічні структури даних
Методи динамічного розподілу пам’яті
Існує три способи динамічного розподілу ОП:
-
За допомогою процедур New і Dispose. Схеатично цей метод можна представити наступним чином:
var r : ^ real;
s : ^ string [3];
l : ^ longint; new (l); new (r); dispose (l); new (s);
...
new (l);
new (r); r ^ r ^ r ^
...
dispose (l); l ^ l ^ s ^
new (s).
Особливості:
-
При кожному черговому створенні динамічного об'єкта пошук місця його розміщення починається з початку Heap-області (значення покажчика HeapOrg). Розміщення об'єкта здійснюється у перший вільний блок підходящого розміру. Так пам’ять під змінну s^ виділена у області, де раніше була розміщена змінна l^, оскільки розмір цієї області підходить для розміщення даної змінної.
-
За допомогою процедур GetMem і FreeMem. Ці процедури використовуються для роботи з нетипізованими покажчиками. На відміну від процедур New-Dispose вони виділяють і звільняють блок Heap-області відповідно до зазначеного розміру. Їх формат:
GetMem (<покажчик>, <розмір>);
FreeMem (<покажчик>, <розмір>);
де <покажчик> - змінна типу pointer,
<розмір> - задається константою типу word або визначається функцією SizeОf, яка повертає як результат розмір ОП, необхідний для збереження зазначеної як параметр змінної.
Наприклад,
var
m : ^ real;
p : pointer;
begin
...
GetMem (p, 4*1024); { р вказує на блок пам'яті 4Кб }
GetMem (m, n* SizeOf (m)); { m вказує на блок пам'яті n* SizeOf (m) }
...
FreeMem (p, 4*1024); { звільнити пам'ять для p }
FreeMem (m, n* SizeOf (m)); { звільнити пам'ять для m }
...
end.
Виклики GetMem і FreeMem повинні відповідати один одному: звільняти треба стільки пам'яті, скільки було виділено, і по тому ж самому покажчику.
-
За допомогою процедур Mark і Relеase. Цей спосіб дає можливість звільняти відразу блок динамічної пам'яті. Для цього спочатку за допомогою процедури
Mark (<покажчик>)
фіксується поточний стан Heap-області (запам'ятовується значення покажчика HeapPtr, який містить адресу початку вільної динамічної пам’яті), а потім за допомогою процедури
Release (<покажчик>)
Heap-область повертається до того стану, який було збережено відповідним викликом Mark (знищуються всі динамічні змінні, створені за допомогою процедури New або GetMem після виклику процедури Mark).
С
хеатично
динамічний розподіл ОП за допомогою
процедур Mark-Relеase
можна представити
наступним чином:
var p : pointer;
q, r1, r2 : ^ real;
s : ^string [11];
begin
new (q);
mark (p);
new (s);
new (r1);
release (p);
new (r2);
end.
Особливості:
-
Специфіка розміщення динамічних об'єктів полягає у тому, що пам'ять у Heap-області виділяється не побайтно, а 8-байтовими порціями. Тому, незалежно від істиного розміру динамічного об'єкту, пам'ять під нього виділяється 8-байтовыми частинами (так для s виділиться 16 байт замість 11).
Стандартні засоби аналізу стану Heap-області
При роботі з Heap-областю, зокрема, для аналізу ресурсів пам'яті перед розміщенням динамічних змінних, можуть використовуватися ряд спеціальних функцій. У мові Pascal це функції:
-
SizeOf
-
повертає як результат розмір пам'яті, необхідний для зберігання зазначеної як параметр змінної (як параметр цієї функції може використовуватися не тільки змінна, але і ідентифікатор типу)
MaxAvail
-
повертає довжину в байтах найбільшої вільної ділянки в Heap-області
MemAvail
-
повертає сумарне значення всіх вільних областей пам'яті в Heap-області
Наприклад,
1) var pl : ^longint ;
begin
...
if MaxAvail >= 4 then New (pl)
else writeln (‘ Динамічну пам'ять вичерпано ‘);
...
2) if MaxAvail >= SizeOf (mas) then p:= New (mas) ;
3) writeln (‘ Розмір пам'яті в Неар-області = ‘, MemAvail) ;
New (р);
writeln (‘ Heap після розміщення р ‘, MemAvail);
Таким чином, загальний алгоритм роботи з динамічними об'єктами в програмі передбачає:
-
оголошення статичного покажчика, в термінах якого будуть задаватися дії над динамічним об’єктом;
-
виділення пам'яті під відповідну динамічну змінну;
-
ініціалізацію покажчика на динамічну змінну (присвоєння йому адреси виділеної під відповідну змінну області динамічної пам'яті);
-
виконання дій з динамічною змінною;
-
звільнення динамічної пам'яті після використання динамічної змінної.
При цьому програміст сам повинен резервувати місце під динамічну змінну, визначати значення покажчиків, звільняти пам'ять (видаляти динамічні змінні).
Звичайно, замість будь-якої статичної змінної можна використовувати динамічну, але без реальної необхідності цього робити не варто. По-перше, змінні простих типів немає сенсу розміщувати в динамічній пам'яті, оскільки вони займають менше місця, ніж покажчик на них. Наприклад, покажчик на ціле займає 4 байти, саме ціле - 2 байти. По-друге, робота з динамічними даними уповільнює виконання програми, оскільки доступ до об'єкта відбувається у два кроки: спочатку шукається покажчик, потім по ньому – сам об'єкт. Тобто, виграш в пам'яті компенсується програшем у часі.
Робота з вказівниками передбачена в багатьох мовах програмування. Наприклад, у мові С покажчики використовуються практично в будь-якій програмі. У Pascal роль покажчиків дещо скромніша.
Приклад. Знаходження суми парних чисел масиву sз 100 цілих чисел.
program Sum_Ch1; program Sum_Ch2;
var i, s : integer; type mas = array [0..100] of integer;
m : array [0..100] of integer; var i, s : integer;
begin m : ^mas;
s:=0; begin
for i:=0 to 100 do s:=0;
if (m[i] mod 2)=0 then s:=s+m[i]; New (m);
writeln (‘s=‘, s); for i:=0 to 100 do
end. if (m^[i] mod 2)=0 then s:=s+m^[i];
Dispose (m);
writeln (‘s=‘, s);
end.
