где P - переменная типа «указатель» требуемого типа.
size - целочисленное выражение размера запрашиваемой памяти в байтах, здесь также может использоваться функция SizeOf(тип_данных).
Эта процедура создает новую динамическую переменную требуемого размера и свойства, а также помещает адрес этой созданной переменной в переменную Ртипа «указатель». Доступ к значению созданной переменной можно получить с помощью P^.
Например:
Type
Rec =record
Field1:string[30];
Field2:integer;
end;
ptr_rec = ^ rec;
Var
p : ptr_rec;
Begin
GetMem ( Р, SizeOf (Rec)); { Выделение памяти;
адрес выделенного участка фиксируется в Р;
размер этой памяти в байтах определяет и возвращает стандартная функция SizeOf, примененная к описанному типу данных; однако, зная размеры внутреннего представления используемых полей, можно было бы подсчитать размер памяти «вручную» и записать в виде константы вместо SizeOf (Rec) }
...
{использование памяти}
...
FreeMem ( p, SizeOf (Rec)); {освобождение уже ненужной памяти}
...
Динамическая память может быть освобождена следующими способами
1. С помощью стандартной процедуры Dispose
(употребляется в паре с New(…)).
Dispose (P);
где P - переменная типа «указатель»
(типизированный).
В результате работы процедуры Dispose(P) участок памяти, связанный с указателем P, помечается как свободный для возможных дальнейших размещений. При этом физической чистки указателя P и связанной с ним памяти нe происходит, поэтому, даже удалив этот экземпляр записи, можно все же получить значения ее полей, однако использовать это обстоятельство категорически не рекомендуется.
2. С помощью стандартной процедуры FRееМеm.
(употребляется в паре с GetMem (…)).
FreeMem (P, size);
где P - переменная типа «указатель»,
size - целочисленное выражение размера памяти в байтах для освобождения.
Эта процедура помечает память размером, равным значению выражения size, связанную с указателем P, как свободную (см. пример для GetMem).
3. Автоматически по завершении всей программы.
Управлять распределением и освобождением оперативной памяти можно следующей парой процедур
Mark (P);
Release (P);
где P - переменная типа «указатель»;
Mark - запоминает в переменной-указателе р состояние
динамической области в данный момент;
Release - освобождает всю динамическую память,
которая выделена процедурами New или Getmem после запоминания текущего значения указателя р с помощью процедуры Mark.
Следует распределять и освобождать память «парными» процедурами:
· GetMem – FreeMem,
· New – Dispose,
· Mark – Release.
Обращения к Mark и Release нельзя чередовать с обращениями к Dispose и Frееmem ввиду различий в их реализации.
Например:
Var
p : pointer;
p1, p2, p3 : ^integer;
Begin
New (p1);
p1^ := 10;
Mark (p) ; {пометка динамической области}
New (p2) ;
p2^ := 25 ;
New (p3) ;
p3^ := p2^ + p1^;
Writeln ( p3^);
Release (p) ; {память, связанная с p2^ и p3^, освобождена, а p1^ может использоваться}
End.
Стандартные функции обработки динамической памяти.
В процессе выполнения программы можно наблюдать за состоянием динамической областидля оценки возможности очередного выделения динамической области требуемого размера.
Для этих целей Турбо Паскаль предоставляет две функции (без параметров).
MaxAvail;
Тип возвращаемого значения - longint.
Функция возвращает размер в байтах наибольшего свободного в данный момент участка в динамической области. По этому размеру можно судить о том, какую наибольшую динамическую память можно выделить.
Type
zap=record
field1: string [20];
field2: real;
end;
Var
p: pointer;
Begin
...
if MaxAvail < SizeOf (zap)
then Writeln ('He хватает памяти!')
else GetMem ( р, SizeOf(zap));
...
Вторая функция:
MemAvail;
Тип возвращаемого значения - longint.
Эта функция возвращает общее число свободных байтов динамической памяти,то есть суммируются размеры всех свободных участков и объем свободной динамической области.
Динамическая область размещается в специально выделяемой области, которая носит название «куча»(heap). Куча занимает всю или часть свободной памяти, оставшейся после загрузки программы. Размер кучи можно установить с помощью директивы компилятора М:
{$М<стек>, <минимум кучи>, <максимум кучи>}
где <стек>- специфицирует размер сегмента стека в байтах. По умолчанию размер стека 16 384 байт, а максимальный размер стека 65 538 байт;
<минимум кучи> - специфицирует минимально требуемый размер кучи в байтах; по умолчанию минимальный размер 0 байт;
<максимум кучи> - специфицирует максимальное значение памяти в байтах для размещения кучи; по умолчанию оно равно 655 360 байт, что в большинстве случаев выделяет в куче всю доступную память; это значение должно быть не меньше наименьшего размера кучи.
Все значения задаются в десятичной или шестнадцатеричной формах. Например, следующие две директивы эквивалентны:
{$М 16384,0,655360}
{$M $4000, $0, $A000}
Если указанный минимальный объем памяти недоступен, то программа выполняться не будет.
Управление размещением в динамической памяти осуществляет администратор кучи, являющийся одной из управляющих программ модуля System.
Примеры и задачи.
Рассмотрим пример размещения и освобождения разнотипных динамических переменных в куче.
Type
st1=string[7];
st2=string[3];
Var
i,i1,i2,i3,i4 : ^integer;
r^ : real;
s1 : ^st1;
s2 : ^st2;
Begin
New (i) ;
i1^:=1;
New(i2);
i2^ := 2 ;
New ( i3 );
i3^=3;
New(i4);
i4^ := 4 ; {1}
Disроsе ( i2 ) ; {освобождается второе размещение}
New ( i ); {память нужного размера (в данном случае два байта)
выделяется на первом свободном месте от начала кучи,
достаточном для размещения данной переменной;
в этом примере - это участок, который занимала
переменная i2^, ее адрес остался в указателе i2 }
i^ := 5 ; {2}
Dispose ( i3 ) ; {освобождается третье размещение}
New ( r ) ; {память под переменную типа real выделяется
в вершине кучи, так как размер дырки с адресом i3
(2 байта) мал для размещения переменной типа real,
для которой необходимо 6 байт }
r^ := 6 ; {3}
writeln ( r^ ) ; { ВЫВОД: 6.0000000000E+00}
END.
В следующем примере используется массив указателей.
r [ i ]^ := Random; {генерация случайных вещественных чисел
в диапазоне 0 <= r[i]^ < 1}
writeln ( r [ i ]^ ); {Вывод случайных чисел
в экспоненциальной форме}
end;
End.
Организация списков.
Преимущества динамической памяти становятся особенно очевидными при организации динамических структур, элементы которых связаны через адреса (стеки, очереди, деревья, сети и т.д.). Основой моделирования таких структур являются списки.
Список - это конечное множество динамических элементов, размещающихся в разных областях памяти и объединенных в логически упорядоченную последовательность с помощью специальных указателей (адресов связи).