Процедуры GetMem и FreeMem
Иногда нежелательно выделять память тем способом, как это делает 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.
Пример. 8.4 Динамическое распределение памяти для строки.
Вместо выделения для строк 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.
Пример 8.5 Простой тип записи.
Каждая запись типа 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));
Это не только обеспечивает, что вы выделяете и освобождаете один и тот же объем, но гарантирует, что при изменении размера типа ваша программа все равно будет выделять нужную память.