Використання динамічних змінних
В процесі компіляції програми для змінних виділяється пам’ять конкретного розміру, яка зберігається за змінними на весь період роботи програми. Такі змінні є статичними, доступ до відповідних областей пам’яті здійснюється за їх іменами.
Такий спосіб не є ефективним, особливо коли невідомо які розміри пам’яті потрібні для зберігання даних. Змінні, які з\’являються в процесі роботи програми або їх розмір може змінюватись при її роботі називають динамічними змінними.
Для таких даних пам’ять виділяється на етапі виконання програми, крім того, така пам’ять може бути визволеною ще до кінця програми.
Динамічна змінна явно не описується, до неї немає доступу по імені. Замість цього використовується спеціальна змінна – вказівник, значенням якого є адреса пам’яті, де розміщується динамічний об’єкт.
Вказівники можна описувати в програмі з допомогою символу ^, наприклад:
Var Vkaz1,Vkaz2:^Integer;
R,Т:^Rеа1;
Такий опис ще не виділяє пам’ять для змінних, він тільки означає, що змінні Vkaz1 та Vkaz2 є вказівниками на дані цілого типу, а змінні R і Т – вказівники на дійсне дане.
Виділення пам’яті здійснюється спеціальним оператором New, наприклад:
New(T);
Після цієї операції вказівник Т придбає значення адреси пам’яті для дійсної змінної. Щоб отримати доступ до цієї області пам’яті, треба знак ^ записати після вказівника, наприклад:
Т^;=16.4;
Writeln(T^);
Над динамічними змінними можна виконувати такі ж операції, як і над статичними, наприклад:
R^:=T^+20;
If R^<30 Then T^:=40;
Вказівники, в свою чергу, можна тільки присвоювати один одному та порівнювати на рівність чи нерівність між собою, наприклад:
T:=R;
If T<>R Then T:=Nil;
Треба бути уважними при вживанні динамічних змінних та вказівників. Розглянемо приклад фрагменту програми:
R^:=10.5;
T^:=16.2;
R:=T;
Якщо замість останнього оператору вжити оператор R^:=Т^, то вказівники будуть зв’язані з різними комірками
пам’яті, в яких записано одне й теж число 16.2.
Динамічною змінною може бути також рядок або масив, наприклад:
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]);
Як бачимо, до елементу масиву можна звертатися, вживаючи ім’я VkazMas^[i].
Якщо в процесі виконання програми яка-небудь змінна стане непотрібною, її можна вилучити з пам’яті за допомогою оператора Dispose, наприклад:
Dispose(VkazMas);
Це значить, що пам’ять, яка була виділена під масив звільнена, а сам вказівник VkazMas матиме невизначене значення.
Подібна операція буває корисною при необхідності економити пам’ять.
В мові Pascal можна, також використовувати нетипизовані вказівники, які, на відміну від типова них, не зв’язані з конкретним типом. Такі вказівники описують, вживаючи слово Pointer, наприклад:
Var Р1, Р2 : Pointer;
Ці вказівники сумісні з будь-якими типизованими вказівниками, тобто вказівник Р1 може одержати, як значення вказівника на ціле число, так і значення вказівника на дійсне число.
Нетипизований вказівник може одержати значення одним з декількох способів.
Процедура GetMem(P1,Size) виділить пам’ять розміром Size байтів, вказівник Р1 матиме значення адреси початку такої пам’яті (величина Size обмежена числом 64К). Це нерідко вживають для зберігання фрагментів зображення на екрані дисплею. Звільнення такої пам’яті виконує процедура FreeMem(Pl,Size).
Функція Addr визначає адресу свого аргументу (змінної або підпрограми), наприклад:
P2:=Addr(A);
Таку операцію використовують, наприклад, при заміні стандартної підпрограми обробки переривання на «свою» підпрограму.
Функція Ptr перетворить свій аргумент-адресу в значення вказівника, наприклад:
P1:=Ptrt($1234,$4567);
Нетипизованому вказівнику можна присвоїти значення спеціальної функції Nil, наприклад:
P1:=NiI;
Функція Nil виробляє значення «пусте» (згадайте пустий рядок, пусту множину). Вона застосовується, як деяка ознака кінця динамічної структури, наприклад, кінця списку.
При програмуванні на мові Pascal для розміщення динамічних змінних використовується область пам’яті з назвою Heap або «куча». Розмір Heap можна регулювати за допомогою директиви компілятору $М або задати при налагодженні середовища (Option/Compiler/Memory size) від 0 до 65535 байт.
Приклад директиви:
{$М 20000,10000,200000}
Перше число показує розмір сегмента стеку (від 1024 до 655356). Це число вибирається в залежності від наявності та виду підпрограм. Друге число визначає найменш допустимий в даній програмі розмір Heap, а останнє число – найбільший розмір цієї пам’яті. Конкретні розміри Heap вибираються в залежності від задачі та кількості динамічних даних.
Приклад:
скласти програму обчислення функції
Програма:
Var X, Y, Z, F : ^Real;
Begin
Write(‘Введіть Y, Z :’);
New(Y);
New(Z);
ReadLn(Y^,Z^);
Write(‘Введіть X :’);
New(X);
ReadLn(X^);
New(F);
IF (X^<Z^) THEN F^:=SQR(X^)+EXP(Z^)/COS(Y^)
ELSE F^:=SIN(X^)+COS(Z^);
Dispose(X);
Dispose(Y);
Dispose(Z);
WriteLn(‘Значення F=’,F);
Dispose(F);
End.