Иногда нежелательно выделять память тем способом, как это делает New. Вам может потребоваться выделить больше или меньше памяти, чем это делает New по умолчанию, либо до начала выполнения вы можете просто не знать, сколько памяти вам нужно использовать. Borland Pascal выполняет такое распределение с помощью процедуры GetMem.
Процедура GetMem воспринимает два параметра: переменную-указатель, для которой вы хотите распределить память, и число распределяемых байт.
Динамическое выделение памяти для строки
Пусть, например, у вас есть прикладная программа, которая считывает 1000 строк из файла и записывает их в динамическую память. Вы не знаете, насколько длинной будет каждая из этих строк, поэтому вам потребуется описать строковый тип такого размера, который будет соответствовать максимальной возможной строке. Если предположить, что не все строки имеют максимальную длину, то у вас будет бесполезно использоваться память.
Чтобы решить эту проблему, вы можете считать каждую строку в буфер, затем выделить столько памяти, сколько требуется для фактических данных в строке. Пример этого показан ниже:
type PString = ^String;
var ReadBuffer: String; LinewRead: array[1..1000] of PString; TheFile: Text; LineNumber: Integer;
begin Assign(TheFile, "FOO.TXT"); Reset(TheFile); for LineNumber := 1 to 1000 do begin Readln(ReadBuffer); GetMem(LinesRead[LineNumber], Length(ReadBuffer) + 1); LinesRead[LineNumber]^ := ReadBuffer; end; end.
Пример 1. Динамическое распределение памяти для строки.
Вместо выделения для строк 256К (256 символов на строку 1000 раз) вы выделили 4К (4 байта на указатель 1000 раз), плюс объем, фактически занимаемый текстом.
Освобождение выделенной памяти
Аналогично тому, как требуется освобождать память, выделенную с помощью New, вам нужно освобождать память, распределенную с помощью процедуры GetMem. Это можно сделать с помощью процедуры FreeMem. Аналогично тому, как каждому вызову New должен соответствовать парный вызов Dispose, каждому вызову процедуры GetMem должен соответствовать вызов FreeMem.
Как и GetMem, процедура FreeMem воспринимает два параметра: освобождаемую переменную и объем освобождаемой памяти. Важно, чтобы объем освобождаемой памяти точно совпадал с объемом выделенной памяти. New и Dispose, основываясь на типе указателя, всегда знают, сколько байт нужно выделять или освобождать. Но в случае GetMem и FreeMem объем выделяемой памяти находится всецело под вашим контролем.
Если вы освободите меньше байт, чем было выделено, то оставшиеся байты теряются (происходит "утечка" динамически распределяемой памяти). Если вы освободите большее число байт, чем было выделено, то можете освободить память, распределенную для другой переменной, что может привести к порче данных. В защищенном режиме освобождение большего объема памяти, чем было выделено, вызовет ошибку по нарушению защиты (GP).
Предположим, например, что вы собираетесь выделить память для одной или более записей данных типа TCheck:
type PCheck = ^ TCheck; TCheck = record Amount: Real; Mounth: 1..12;
Day: 1..31; Year: 1990..2000; Payee: string[39]; end.
Пример 2. Простой тип записи.
Каждая запись типа TCheck занимает 50 байт, поэтому, если у вас есть переменная ThisCheck типа PCheck, вы можете распределить динамическую запись следующим образом:
GetMem(ThisGheck, 50);
а позднее освободить ее с помощью вызова:
FreeMem(ThisCheck, 50);
Использование с процедурой GetMem функции SizeOf
Однако убедиться, что вы каждый раз выделяете и освобождаете один и тот же объем памяти, недостаточно. Вы должны обеспечить распределение правильного объема памяти. Предположим, вы изменили определение TCheck. Например, если вы переопределили TCheck.Payee как 50-символьную строку вместо 39-символьной, то не сможете получить и освобождать достаточно памяти. Надежнее всего использовать в программе функцию SizeOf, например:
GetMem(ThisCheck, SizeOf(TCheck)); . . .
FreeMem(ThisCheck, SizeOf(TCheck));
Это не только обеспечивает, что вы выделяете и освобождаете один и тот же объем, но гарантирует, что при изменении размера типа ваша программа все равно будет выделять нужную память.
Проверка объема доступной динамически распределяемой памяти
В Borland Pascal определены две функции, возвращающие важную информацию о динамически распределяемой области памяти: MemAvail и MaxAvail.
Функция MemAvail возвращает общее число байт, доступных для распределения в динамической памяти. Перед выделением большого объема в динамически распределяемой памяти полезно убедиться, что такой объем памяти доступен.
Функция MaxAvail возвращает размер наибольшего доступного блока непрерывной памяти в динамически распределяемой области. Первоначально при запуске программы MaxAvail равно MemAvail, поскольку вся динамически распределяемая область памяти является доступной и непрерывной. После распределения нескольких блоков памяти пространство в динамически распределяемой области скорее всего станет фрагментированным. Это означает, что между частями свободного пространства имеются распределенные блоки. Функция MaxAvail возвращает размер наибольшего свободного блока.
8 8 8
| |