Выделение и освобождение динамической памяти




Ссылки и указатели

Каждая переменная, описанная в разделе var, имеет свое имя и описание. Описание, среди прочего, позволяет компилятору определить, сколько байтов памяти необходимо выделить в памяти для переменной. Имя же позволяет обратиться к зарезервированной под переменную ячейке памяти.

Однако, каждая переменная, кроме своего имени, имеет и физический адрес: номер ячейки памяти, в которой хранится значение переменной.

Понятие указателя

Указатель представляет собой переменную целого типа, которая интерпретируется как адрес байта памяти, содержащей некоторый элемент данных. Этим элементом может быть переменная, константа, адрес другой переменной и т.д.

Адрес интерпретируется двумя шестнадцатибитными словами BA: BS, где BAсегментный адрес, BSсмещение. Сегмент – участок памяти, длиной 64 Кбайта.

При обращении к переменной по имени программа сама определяет, по какому адресу находится ее значение. Указатель же позволяет хранить адрес произвольной ячейки памяти, в которой могут хранится те или иные данные.

Описание указателей

Существует два типа указателей: типизированные и нетипизированные.

Типизированные указатели хранят адрес данных первого байта какого-либо конкретного типа. Для объявления таких указателей обычно используется символ ^, который размещается непосредственно перед типом данных, на значения которого ссылается типизированный указатель:

Пример:

type

IntPtr = ^Integer;

var

P1 = IntPtr;

P2 = ^Integer;

Отметим, что типизированные указатели – это единственный тип в языке Паскаль, который может ссылаться на еще не описанный тип.

Пример:

type

Link = ^Node; {тип Node будет описан только в следующей строке!}

Node = record

Key: Integer;

Next: Link;

end;

Нетипизированные указатели хранят адрес данных, не связанные с каким-либо конкретным типом данных. Они могут ссылаться и на целочисленные данные, и на символьные, и т.д. В этом случае используется ключевое слово Pointer.

Пример:

var

P: Pointer;

Такая переменная представляет собой адрес байта данных, начиная с которого хранятся какие-либо данные произвольной длины.

Операции с указателями

Определение адреса

Физический адрес любой переменной можно узнать при помощи стандартной функции:

addr(<имя_переменной>): <указатель>

или унарной операции

@ <имя_переменной>.

Пример:

var

P: Pointer;

intP: ^integer;

x: integer;

P:= addr(x);

P:= @x;

intP:= addr(x);

intP:= @x;

Разыменование

Указатель – это лишь адрес ячейки памяти. Для обращения к содержимому такой ячейки используют операцию, называемую разыменованием. Записывается такая операция следующим образом:

<имя_указателя> ^

Отметим, что операция разыменования применима лишь к типизированным указателям.

Пример:

var

intP: ^integer;

x: integer;

intP:= @x;

intP^:= 10;

writeln(intP^);

Присваивание

В случае указателей при присваивании получают лишь новый адрес памяти. Содержимое самих ячеек памяти остается неизменным.

Пример:

var

intP1: ^integer;

intP2: ^integer;

x:= 3;

intP1:= @x;

y:= 4;

intP2:= @y;

intP1:= intP2; {оба указателя ссылаются на значение 4}

intP2^:= 7; {оба указателя ссылаются на значение 7}

В операции присваивания могут фигурировать только указатели, адресующие переменные одинаковых типов данных. Обойти эти ограничения позволяет универсальность нетипизированного указателя pointer, совместимого с указателями любых типов:

Пример:

var

intP1: ^integer;

intP2: ^integer;

realP: ^real;

P: Pointer;

intP1:= intP2;

{нельзя realP:= intP1}

P:= intP1;

realP:= P;

У указателей также существует свой "ноль", который означает, что указатель не указывает никуда:

p:= nil;

Сравнение

Для указателей определены две операции сравнения: равно (=) и не равно (<>).

Две переменные, описанные как указатели на один и тот же тип данных, считаются совпадающими, если они указывают на одну и ту же область памяти.

Для разнотипных указателей сравнения невозможны. Однако сравнивать типизированный и нетипизированный указатели можно.

Выделение и освобождение динамической памяти

Указатели – это лишь адрес ячейки памяти, а о состоянии самой ячейки должен позаботиться программист.

Вся динамическая память может располагаться как сплошной массив, состоящий из байтов, и называемый кучей. Куча размещается в памяти компьютера следом за областью памяти, которую занимает тело программы.

Перед помещением данных в кучу, программист должен подготовить участок памяти, куда данные собственно будут помещены. После завершения использования данных, использованное место необходимо за собой очистить.

Процедура New(P) резервирует в памяти участок для значения того типа, на который ссылается указатель P, и устанавливает в указатель адрес зарезервированного участка.

Пример:

var

P1: ^Integer;

...

New(P1);

P1^:=5;

Процедура Dispose(P) освобождает участок памяти, на которую указывает типизированный указатель P.

Пример:

Dispose(P1);

P1:=nil;

Процедуры New и Dispose применимы только для типизированных указателей. При работе с нетипизированными указателями можно воспользоваться следующими процедурами.

Процедура GetMem(P, Size) выделяет из кучи блок памяти размером Size (максимум 64 Кбайта) и устанавливает в указатель P ссылку на неё.

Процедура FreeMem(P, Size) уничтожает в куче блок памяти размером Size по адресу P.

 

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2017-12-29 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: