Выделение и освобождение динамической памяти
Вся динамическая память в Турбо Паскале рассматривается как сплошной массив байтов, который называется кучей (heap). Физически куча располагается в старших адресах сразу за областью памяти, которую занимает тело программы.
Начало кучи хранится в стандартной переменной heaporg, конец – в переменной heapend. Текущую границу незанятой динамической памяти указывает указатель heapptr.
Память под любую динамически размещаемую переменную выделяется процедурой new. Параметром обращения к этой процедуре является типизированный указатель. В результате обращения указатель приобретает значение, соответствующее динамическому адресу, начиная с которого можно разместить данные, например:
Var
i, j: ^integer;
r: ^real;
Begin
New(i);
…
End.
После выполнения этого фрагмента указатель i приобретет значение, которое перед этим имел указатель кучи heapptr, а сам heapptrувеличит свое значение на 2, так как длина внутреннего представления типа integer, с которым связан указатель i, составляет 2 байта (на самом деле это не совсем так: память под любую переменную выделяется порциями, кратными 8 байтам). Оператор
New(r);
вызовет еще раз смещение указателя heapptr, но теперь уже на 6 байт, потому что такова длина внутреннего представления типа real. Аналогичным образом выделяется память и для переменной любого другого типа.
После того как указатель приобрел некоторое значение, т.е. стал указывать на конкретный физический байт памяти, по этому адресу можно разместить любое значение соответствующего типа. Для этого сразу за указателем без каких—либо пробелов ставится значок ^, например:
i^ := 2; {В область памяти i помещено значение 2}
r^ := 6.5; {В область памяти r помещено значение 6.5}
Таким образом, значение, на которое указывает указатель, т.е. собственно данные, размещенные в куче, обозначаются значком ^, который ставится сразу за указателем. Если за указателем нет значка ^, то имеется в виду адрес,по которому размещены данные.
Динамически размещенные данные можно использовать в любом месте программы,где это допустимо для констант и переменных соответствующего типа, например:
r^ := sqr(r^) + i^ - 17;
Разумеется, совершенно недопустим оператор
r := sqr(r^) + i^ - 17;
так какуказателю r нельзя присвоить значение вещественного выражения.Точно так же недопустим оператор
r^ := sqr(r);
поскольку значением указателя r является адрес, иего (в отличие от того значения, которое размещено поэтому адресу) нельзя возводить в квадрат, ошибочным будет и такое присваивание:
r^ : = i ;
Динамическую память можно не только забирать из кучи, но и возвращать обратно. Для этого используется процедура Dispose. Например, операторы
Dispose (r);
Dispose (i);
вернут в кучу 8 байт, которые ранее были выделены указателям iиr.
Процедура Dispose(ptr)не изменяет значения указателя, а лишь возвращает в кучу память, ранее связанную с этим указателем. Однако повторное применение процедуры к свободному указателю приведет к возникновению ошибки периода исполнения. Освободившийся указатель программист может пометить зарезервированным словом nil.
Другая возможность состоит в освобождении целого фрагмента кучи. С этой целью перед началом выделения динамической памяти текущее значение указателя heapptrзапоминается в переменной—указателе с помощью процедуры Mark. Теперь можно в любой момент освободить фрагмент кучи, начиная от того адреса, который запомнила процедура Mark, и до конца динамической памяти. Для этого используется процедура Release. Например:
Var
p, pl, p2,
p3, p4, p5 : ^integer;
Begin
New (pl);
New (p2);
Mark (p);
New (p3);
New (p4);
New (p5)
Release (p);
End.
В этом примере процедурой Mark(p)в указатель p было помещено текущее значение heapptr, однако память под переменную не резервировалась. Обращение Release(p)освободило динамическую память от помеченного места до конца кучи.
Как уже отмечалось, параметром процедуры Newможет быть только типизированный указатель. Для работы с нетипизированными указателями используются процедуры:
GetMem (p, size) – резервирование памяти;
FreeMem (p, size) – освобождение памяти.
Здесь р — нетипизированный указатель;
size — размер в байтах требуемой или освобождаемой части кучи.
За одно обращение к куче процедурой GetMemможно зарезервировать до 65521 байта динамической памяти.
Использование процедур GetMem и FreeMem, как и вообще вся работа с динамической памятью, требует особой осторожности и тщательного соблюдения простого правила: освобождать нужно ровно столько памяти, сколько ее было зарезервировано, и именно с того адреса, с которого она была зарезервирована.
Дата добавления: 2017-01-29; просмотров: 797;