Тип указатель определяет адрес памяти для переменной базового типа.
Тип_указатель::= ("^" базовый_тип) | "Pointer"
Базовый_тип::= ИД типа.
Указатель может быть типированным, то есть указывать на переменную некоторого типа, и может быть нетипированным (pointer), то есть указывать на переменную произвольного типа. Если указатель имеет нулевое значение, его значение равно nil. В независимости от типа указателя, его размер в памяти компьютера постоянен и равен 4 байтам. Для обращения к переменной, адрес которой хранится в указателе, требуется после имени указателя ставить знак "^".
В ТП существует специальная операция @ (см. параграф 1.4.3.6), с помощью которой можно получить указатель на любую переменную, процедуру или функцию
Пример:
var a:integer;
pa1,pa2:^integer;
begin
pa1:=@a; {получаем адрес переменной a и присваиваем его
указателю Pa1}
pa2:=pa1; {присвоили указателю Pa2 значение указателя Pa1. Теперь Pa1 и Pa2 указывают на одну и ту же область памяти}
pa1^:=7;
pa2^:=6;
writeln(a,' ',pa1^,' ',pa2^);
end.
В результате работы программы на экране появятся три числа 6.
Оперативная память компьютера, выделенная операционной системой для программы, делится на несколько частей:
1) Область кода, где хранится исполняемая часть программы (максимум 64К на модуль).
2) Область стека, где хранятся вызовы и локальные переменные процедур и функций (максимум 64К).
3) Область статических данных, где хранятся все глобальные переменные и размер которой равен 64К.
4) Область динамических данных или куча (вся оставшаяся память, запрошенная программой у операционной системы и объем которой в реальном режиме может в 8-10 раз превышать объем области статических данных, а в защищенном - достигать 16 мегабайт). ТП позволяет произвольно выделять память из кучи блоками любого размера до 64К, а после использования - возвращать обратно.
Динамическая переменная - это переменная-указатель, оперативная память для которой выделяется из области динамических данных во время выполнения программы. По окончании использования переменной память должна быть освобождена, чтобы в дальнейшем ее можно было выделить для другой динамической переменной.
Процедуры для работы с динамической памятью
Выделение памяти для динамических переменных |
procedure New(var P: Pointer); |
Процедура New выделяет память для динамической переменной P. Объем выделяемой памяти рассчитывается на основе типа переменной. Таким образом, требуется, чтобы переменная P была типированным указателем. |
procedure GetMem(var P: Pointer; Size: Word); |
Процедура GetMem выделяет память объемом в Size байт для динамической переменной P, которая может быть указателем любого типа. Процедуры New и GetMem могут стать причиной аварийной остановки программы, если размер затребованной памяти превышает размер максимального свободного блока. |
Освобождение памяти, выделенной ранее для динамических |
procedure Dispose(var P: Pointer); |
Процедура Dispose освобождает динамическую переменную P. Используется в паре с процедурой New. |
procedure FreeMem (var P: Pointer; Size: Word); |
Процедура FreeMem освобождает динамическую переменную P объемом в Size байт. Используется в паре с процедурой GetMem. По завершении работы программы вся память, выделенная из кучи, автоматически освобождается. |
Альтернативный метод освобождения динамической памяти |
procedure Mark (var p: pointer); |
Процедура Mark сохраняет состояние кучи в указателе P. Используется совместно с процедурами New и GetMem. |
procedure Release(var p: pointer); |
Процедура Release освобождает все динамические переменные, память для которых была выделена после использования Mark с указателем P. Процедуры Mark и Release не должны использоваться совместно с Dispose и FreeMem. Мы вообще не советуем пользоваться этими процедурами, как устаревшими и включенными в язык только для совместимости с предыдущими версиями. |
Состояние динамической памяти |
function MemAvail: Longint; |
Процедура Memavail возвращает объем свободной памяти в куче. |
function MaxAvail: Longint; |
Процедура MaxAvail возвращает объем максимального свободного блока памяти в куче. В идеальном случае значения, возвращаемые процедурами MemAvail и MaxAvail, равны. Однако при частом выделении и освобождении блоков возможна дефрагментация кучи. В самом худшем случае может оказаться. что нет возможности выделить память для указателя, несмотря на то, что общий объем кучи превышает затребованный в несколько десятков раз. |
Адресация памяти |
ТП позволяет напрямую взаимодействовать с адресами памяти компьютера. Каждый байт памяти IBM PC адресуется в реальном режиме с помощью двух значений - сегмента и смещения. Следующие функции преобразовывают адреса переменных в сегмент и смещение, а сегмент и смещение - в указатели. Получение сегмента и смещения любого объекта ТП может оказаться полезным при прямом обращении к памяти с помощью предопределенных массивов Mem, MemW и MemL (см. параграф 1.7.2.1) |
function Seg(X): Word; |
Функция Seg возвращает сегментную часть адреса объекта X |
function Ofs(X): Word; |
Функция Ofs возвращает смещение объекта X |
function Ptr(Seg, Ofs: Word): Pointer; |
Функция Ptr возвращает указатель, сформированный из сегмента и смещения |
function Addr(X): pointer; |
Функция Addr возвращает указатель на объект X |
Этапы работы с динамической переменной:
1) Определение ДП.
2) Выделение памяти для ДП или присваивание ей адреса некоторого объекта (переменной, процедуры или функции, произвольной области памяти и т.д.).
3) Работа с динамической переменной.
4) Освобождение памяти, выделенной для динамической переменной.
Например:
type Arr=Array[1..20000] of integer; {определяем тип массива размером в 40000 байт }
var a:array[1..10000]of ^arr;{определяем массив указателей на массив}
i,n:integer;
begin
n:=0;
writeln('Исходный объем динамической памяти: ', MemAvail,
' байт');
while MaxAvail>=sizeof(arr) do begin {пока объем кучи достаточен}
inc;{переходим к следующему элементу массива}
New(a[n]);{выделяем память}
end;
writeln('Удалось выделить память только для ', n,
' элементов массива');
writeln('Осталось памяти ', MemAvail, ' байт');
for i:=1 to n do dispose(a[i]);{освобождаем массив динамических переменных}
readln;
end.