В Delphi я вижу несколько похожих функций, которые можно использовать для выделения памяти, например GetMem и AllocMem. Каковы различия между ними?
Я прочитал документ и только обнаружил, что GetMem не инициализирует память после выделения, а AllocMem делает.
Тогда нужно ли инициализировать память после вызова GetMem? Док говорит да. Но я вижу в некоторых исходных кодах Delphi, что они не вызывают Initialize.
И нужно ли дорабатывать память после окончания использования? Я вижу в некоторых исходных кодах Delphi, что они делают, но что-то не делают.
Спасибо
Логика кажется простой — если вам нужен буфер с нулевой инициализацией, вы можете использовать AllocMem
.
Если вы в любом случае заполняете буфер собственными данными и никогда не используете содержимое по умолчанию - вы можете использовать GetMem
.
Разница в том, что AllocMem
заполняет только что выделенный буфер нулями, а GetMem
— нет. Если ваш код требует, чтобы вновь выделенный буфер изначально был полностью нулевым, вы можете использовать AllocMem
вместо того, чтобы вручную записывать нули в буфер; если вас не волнуют начальные байты в буфере, вы можете сделать (вероятно) дешевле GetMem
.
Например,
var
p: PByte;
begin
GetMem(p, 1024);
try
p^ := 20;
(p + 1)^ := 30;
(p + 2)^ := p^ + (p + 1)^;
ShowMessage((p + 2)^.ToString);
finally
FreeMem(p);
end;
end;
действителен и всегда будет отображаться 50
, но
GetMem(p, 1024);
try
p^ := 20;
(p + 2)^ := p^ + (p + 1)^;
ShowMessage((p + 2)^.ToString);
finally
FreeMem(p);
end;
может отображать что угодно — все зависит от того, какой байт оказался в p + 1
во время выполнения кода (шанс).
Если вы начнете с заполнения вашего буфера нулями, как в
GetMem(p, 1024);
try
FillChar(p^, 1024, 0);
p^ := 20;
(p + 2)^ := p^ + (p + 1)^;
ShowMessage((p + 2)^.ToString);
finally
FreeMem(p);
end;
вы гарантированно увидите 20
, так как p + 1
будет держать 0
.
В качестве альтернативы вы можете сделать
p := AllocMem(1024);
try
p^ := 20;
(p + 2)^ := p^ + (p + 1)^;
ShowMessage((p + 2)^.ToString);
finally
FreeMem(p);
end;
поскольку документация гарантирует, что AllocMem
устанавливает каждый байт во вновь выделенном буфере в 0
.
Но, конечно, выделение памяти вручную в куче предназначено для («продвинутых») низкоуровневых вещей; чаще всего вы этого не делаете. Если вы это сделаете, вы должны знать о таких вещах, как внутренние форматы данных.
Зависит от ваших потребностей. Вам нужен просто буфер и вам все равно, что у него изначально? Используйте GetMem.
GetMem allocates a block of the given Size on the heap, and returns the address of this memory in parameter P. The bytes of the allocated buffer are not set to zero. To dispose of the buffer, use FreeMem. If there isn't enough memory available to allocate the block, an EOutOfMemory exception is raised.
Note: If the memory needs to be zero-initialized, use AllocMem instead.
Если ваша логика предполагает, что все байты этого буфера равны нулю, используйте AllocMem.
AllocMem allocates a block of the given Size on the heap, and returns the address of this memory. Each byte in the allocated buffer is set to zero. To dispose of the buffer, use FreeMem. If there isn't enough memory available to allocate the block, an EOutOfMemory exception is raised.
Note: If the memory does not need to be zero-initialized, it is more efficient to use GetMem instead.
//И нужно ли мне дорабатывать память после окончания ее использования? Говоря о распределении памяти в целом, память, которую вы выделяете, всегда должна быть освобождена.
Есть несколько исключений из этого -
Спасибо. Я просто заметил, что есть процедуры Initialize и Finalize для инициализации и завершения буфера. Когда следует вызывать процедуру Finalize?
Почему вы чувствуете потребность вызвать Finalize
? Что именно нужно доработать? Для массивов вы используете динамические массивы, и они финализируются автоматически. Если вам нужно использовать динамическое размещение для записей, вы используете New
и Dispose
и таким образом завершаете их. Почему ты вообще звонишь GetMem
или AllocMem
? Никто не может предложить руководство без четкого понимания контекста?
Меня не очень волнует этот ответ из-за заключительной части. Любая память, выделенная вручную, должна быть освобождена вручную. Вы говорите, что «исключений мало», но это неправда. Исключений нет. Если вы выделяете с помощью GetMem
или AllocMem
, то вы должны называть FreeMem
. Если вы выделяете с помощью New
, вы должны вызвать Dispose
. Элементы с подсчетом ссылок, такие как строки, динамические массивы, интерфейсы, анонимные методы и т. д., не выделяются с помощью GetMem
, AllocMem
или New
. Что касается передачи права собственности, то это несколько ортогонально. Это просто делегирует ответственность.
@DavidHeffernan В прошлой части я говорил в более общем смысле, чем только AllocMem или GetMem.
Однако это не совсем ясно, и вопрос явно заключается в ручном выделении памяти. Возможно, некоторая неясность на самом деле является результатом вопроса о завершении. Я не думаю, что спрашивающий имеет в виду освобождение, когда говорит завершение. Я думаю, что он имеет в виду финализацию с точки зрения управляемых типов. Но если мы собираемся говорить об управляемых типах, то это должно быть явно указано в вопросе, и, кроме того, управляемые типы не должны выделяться с помощью GetMem
или AllocMem
.
Какой-то странный вопрос, учитывая, что вы даете ответ в тексте вашего вопроса.