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

Адреси та вказівники

Пам'ять комп'ютера можна розглядати як послідовність байтів з номерами 0,1,2..; ці номери називаються адресами. Кожна змінна займає залежно від її типу певну кількість послідовних байтів пам'яті.

Адреса змінної — це адреса її першого байта.

Нехай Т — деякий тип. Вираз ^Т позначає тип, значеннями якого є адреси змінних типу Т.

Наприклад, ^integer позначає тип адрес змінних типу integer, а ^аггау [ 1..100] of char — тип адрес масивів, які мають по 100 символів.

Тип ^Т називається типом адрес даних типу Т або типом посилань на тип Т, а Т базовим для нього.

Адреса змінної х повертається з виклику функції addr вигля­ду addr (x) або є результатом операції @: @x.

Ім'я nil позначає адресу, яка не може бути адресою жодної змінної, тобто вона «нічийна» або неіснуюча. До однотипних ад­рес застосовуються операції порівняння на рівність = і нерівність < >.

Змінні, значеннями яких є адреси, називаються вказівника­ми. Змінна типу ^Т називається типізованим вказівником (вка­зівником типу Т). Вказівнику типу Т можна присвоювати адре­си змінних типу Т. Коли адреса змінної присвоюється вказівни­ку, то кажуть, що він установлюється на змінну.

Приклад. Нехай діють такі оголошення:

type aint = array[1..5] of integer;

paint = ^aint; ppaint = ^paint;

var x : aint; p : paint; pp : ppalnt;

Вказівник p встановлюється на змінну x оператором p: = addr (x) або p: =@x, a pp на p — оператором pp: =addr (p) або рр:=@р.

Рис. 1. Установка вказівників

До вказівників застосовують операцію розіменування^: якщо р — вказівник типу ^Т, то вираз р^ задає змінну типу Т, на яку встановлено р. Отже, якщо р встановлено на змінну х, то вирази хір^ еквівалентні. У наведеному прикладі елемент масиву з індек­сом k позначається як виразом х[к], так і виразами р^[к] або рр^^[к], тобто присвоювання р^[1] :=1 та рр^^[1]:=1 означають те саме, що й х[1]:=1.

  • Розіменування вказівника зі значенням nil (вказівника, не встановленого ні на що) призводить до аварійного завершен­ня програми.

  • У мові Турбо Паскаль вказівник будь-якого типу займає 4 байти, може мати 232= 4 294 967 296 різних станів і вказувати на 4 гігабайти оперативної пам'яті.

Окрім типізованих вказівників, у мові Turbo Pascal є тип Pointer, значеннями якого є довільні адреси. Цей тип суміс­ний за присвоюванням та порівнянням з іншими типами вка­зівників. Змінні типу Pointer називаються безтиповими вка­зівниками.

Їх використання подано нижче.

  • Тип адрес Pointer збільшує гнучкість і потужність мови, але знижує рівень надійності програмування.

  • У мові Turbo Pascal передбачено засоби явного позначення адрес, але тут вони не розглядаються.

Вільна пам'ять

Хворий, який сидів на ліжку в глибині палати, підвівся...

й мученицьким голосом закричав

На волю! На волю! У пампаси!

Ілля Ільф, Євген Петров

Основним призначенням вказівників є робота з вільною па­м'яттю.

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

  • пам'ять, яку займає код програми (виконувані команди);

  • пам'ять, яку займають бібліотеки, підключені до про­грами;

  • статична пам'ять (для статичних змінних програми й мо­дулів);

  • автоматична пам'ять (для автоматичних змінних під час виконання викликів підпрограм);

  • вільна пам'ять, або купа.

Вільна пам'ять відрізняється від інших тим, що її ділянки виділяються під змінні й звільняються від них за явними вказів­ками у Паскаль-програмі (динамічно). Змінні в ній не мають імен у програмі, ідентифікуються за допомогою встановлених на них вказівників і називаються динамічними.

Створення та знищення динамічних змінних називається ке­руванням купою.

Найпростішими процедурами створення та знищення динамі­чних змінних є процедури new та dispose. Їх виклики мають виг­ляд new (р) та dispose (р), де р — типізований вказівник. Сам вказівник може бути автоматичною, статичною або динамічною змінною.

При виконанні виклику new (р), де р — вказівник типу Т, відокремлюється вільна, тобто не зайнята іншими даними, ділян­ка купи. її довжиною є кількість байт, які займають дані типу Т. Адреса першого байта ділянки присвоюється р, тобто р встанов­люється на цю ділянку.

Наприклад, якщо змінну р оголошено як вказівник на масив із 5 цілих змінних, то результат виконання new (р) можна пода­ти, як на рис. 2 (на с. 6). Після установки динамічна змінна по­значається виразом р^.

Якщо в купі вільна ділянка потрібного розміру відсутня, ви­конання програми завершується аварійно. При створенні вели­кої кількості динамічних змінних, коли є ризик вичерпати вільну пам'ять, можна уникнути аварійного завершення за допомогою функції maxavail. її виклик (без параметрів) повертає розмір найбільшої неперервної ділянки купи — значення типу longint.

Наприклад, виділяти пам'ять під дані типу Т й установлюва­ти на неї вказівник р типу ^Т можна так:

if sizeof(Т)<=maxavail {якщо є потрібна ділянка)

then new(р) {то виділимо для неї пам'ять)

{інакше виділити пам'ять неможливо)

else begin

writeln('Ділянки в ',sizeof(Т),' байт немає');

... {дії, пов'язані з відсутністю ділянки)

end;

При виконанні виклику dispose (р) ділянку пам'яті, на яку встановлено р, буде звільнено, але значення р не зміниться. Спро­ба використання звільненої ділянки пам'яті, зокрема, її звільнен­ня, завершується аварійно, як, наприклад, виконання таких

new(р); ... dispose(р);

р^:=... {аварійне закінчення}

або таких операторів.

new(p); q:=p; ...

dispose(р);

dispose(q) {аварійне закінчення}

  • Розміри автоматичної та вільної пам'яті можна змінювати за допомогою підпункту Memory sizes” пункту «Options» меню системи Turbo Pascal, хоча розміри вільної пам'яті за узгоджен­ням є максимально можливими.

  • Загальний розмір незайнятої частини купи повертається з виклику функції memavai1 (також без параметрів).