Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООП_самостійне_опрацюв.doc
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
1.22 Mб
Скачать

1. Вказівники та динамічна пам'ять. Динамічна пам'ять

Динамічна пам'ять – це оперативна пам'ять ПК, що надається програмі при її роботі. Динамічне розміщення даних означає використовування динамічної пам'яті безпосередньо при роботі програми. На відміну від цього статичне розміщення здійснюється компілятором Object Pascal в процесі компіляції програми. При динамічному розміщенні наперед не відомий ні тип, ні кількість розміщуваних даних.

Вказівники

Оперативна пам'ять ПК є сукупністю осередків для зберігання інформації – байтів, кожний з яких має власний номер. Ці номери називаються адресами, вони дозволяють звертатися, до будь-якого байта пам'яті. Object Pascal надає в розпорядження програміста гнучкий засіб управління динамічною пам'яттю – так звані покажчики. Покажчик – це змінна, яка як свого значення містить адресу байта пам'яті. За допомогою покажчиків можна розміщувати в динамічній пам'яті будь-який з відомих в Object Pascal типів даних. Лише деякі з них (Byte, Char, ShortInt, Boolean) займають у внутрішньому уявленні один байт, інші – декілька суміжних. Тому насправді покажчик адресує лише перший байт даних.

Як правило, покажчик зв'язується з деяким типом даних. Такі покажчики будемо називати тими, що типізуються. Для оголошення покажчика, що типізується, використовується значок ^, який поміщається перед відповідним типом, наприклад:

var

p1 : ^Integer;

р2 : ^Real;

type

PerconPointer = "PerconRecord;

PerconRecord = record Name : String;

Job : String;

Next : PerconPointer ,

end;

Зверніть увагу: при оголошенні типу PerconPointer ми послалися на тип PerconRecord, який заздалегідь в програмі оголошений не був. Як вже наголошувалося, в Object Pascal послідовно проводиться в життя принцип, відповідно до якого перед використовуванням якого-небудь ідентифікатора він повинен бути описаний. Виключення зроблено тільки для покажчиків, які можуть посилатися на ще не оголошений тип даних.

В Object Pascal можна оголошувати покажчик і не зв'язувати його при цьому з яким-небудь конкретним типом даних. Для цього служить стандартний тип pointer, наприклад:

var

р: Pointer;

Покажчики такого роду будемо називати нетипізованими. Оскільки покажчики, що нетипізуються, не пов'язані з конкретним типом, з їх допомогою зручно динамічно розміщувати дані, структура і тип яких міняються в ході роботи програми.

Як вже говорилося, значеннями покажчиків є адреси змінних в пам'яті, тому слід було б чекати, що значення одного покажчика можна передавати іншому. Насправді це не зовсім так. В Object Pascal можна передавати значення тільки між покажчиками, пов'язаними з одним і тим же типом даних.

Якщо, наприклад

var

pI1,pI2: ^integer;

pR: ^Real;

p: Pointer;

pI1 := pI2;

pl1 :=pR;

p := pR;

pI1 := p;

і тим самим досягти потрібного результату.

Виділення і звільнення динамічної пам'яті

Вся динамічна пам'ять в Object Pascal розглядається як суцільний масив байтів, який називається купою.

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

var pI,pJ: ^Integer;

pR: ^Real;

begin

New (pI) ;

New (pR) ;

end;

Після того, як покажчик набув деяке значення, тобто став указувати на конкретний фізичний байт пам'яті, за цією адресою можна розмістити будь-яке значення відповідного типу. Для цього в операторі привласнення відразу за покажчиком без яких-небудь пропусків ставиться значок ^, наприклад:

PJ^ := 2; // В область пам'яті pJ поміщено значення 2

PR^ := 2*pi; // В область пам'яті pR поміщено значення 6.28

Таким чином, значення, на яке указує покажчик, тобто власне дані, розміщені в купі, позначаються значком ^, який ставиться відразу за покажчиком. Якщо за покажчиком немає значка ^, то мається на увазі адреса, по якій розміщені дані. Має сенс ще раз задуматися над тільки що сказаним: значенням будь-якого покажчика є адреса, а щоб вказати, що йдеться не про адресу, а про ті дані, які розміщені за цією адресою, за покажчиком ставиться ^ (іноді про це говорять як про розіменування покажчика).

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

рR^ := Sqr(pR") + І^ - 17;

Зрозуміло, абсолютно недопустимbq оператор

pR := Sqr(pR") + I^ - 17;

оскільки покажчику pR не можна привласнити значення речовинного виразу. Так само недопустимий оператор

pR ^ := Sqr(pR); оскільки значенням покажчика pR є адреса і його (на відміну від того значення, яке розміщене за цією адресою) не можна зводити в квадрат. Помилковим буде і таке привласнення:

pR^' := pJ;

оскільки речовинним даним, на які указує pR, не можна привласнити значення покажчика (адреса).

Динамічну пам'ять можна не тільки забирати з купи, але і повертати назад. Для цього використовується процедура Dispose. Наприклад, оператори

Dispose(pJ);

Dispose(pR);

повернуть в купу пам'ять, яка раніше була закріплена за покажчиками pJ і pR (див. вище).

Помічу, що процедура Dispose (pPtr) не змінює значення покажчика pPtr, а лише повертає в купу пам'ять, раніше пов'язану з цим покажчиком. Проте повторне застосування процедури до вільного покажчика приведе до виникнення помилки періоду виконання. Покажчик, що звільнився, програміст може помітити зарезервованим словом nil. Чи помічений який-небудь покажчик чи ні, можна перевірити таким чином:

const

pR: ^Real = NIL;

begin

if pR = NIL then

New (pR) ;

Dispose(pR) ;

pR := NIL;

end;

Ніякі інші операції порівняння над вказівниками не дозволені.

Приведений вище фрагмент ілюструє переважний спосіб оголошення покажчика у вигляді константи, що типізується, з одночасним привласненням йому значення nil. Слід врахувати, що початкове значення покажчика (при його оголошенні в розділі змінних) може бути довільним. Використовування покажчиків, яким не привласнено значення процедурою New або іншим способом, не контролюється Delphi і викличе виключення.

Як вже наголошувалося, параметром процедури New може бути покажчик, що тільки типізується. Для роботи з покажчиками, що нетипізуються, використовуються Процедури GetMem І FreeMem:

GetMem(P, Size); // резервування пам'яті;

FreeMem(P, Size); // звільнення пам'яті.

Тут р – покажчик, що нетипізується; size – розмір в байтах необхідної або звільняється частини купи.

Примітка

Використання процeдуp GetMem/FreeMemMem, як і взагалі вся робота динамічної пам'яттю, вимагає особливої обережності і ретельного дотримання простого правила: звільняти потрібно рівно стільки пам'яті, скільки її було зарезервовано, і саме з тієї адреси, з якої вона була зарезервована.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]