В чем разница между GetMem и AllocMem?

В Delphi я вижу несколько похожих функций, которые можно использовать для выделения памяти, например GetMem и AllocMem. Каковы различия между ними?

Я прочитал документ и только обнаружил, что GetMem не инициализирует память после выделения, а AllocMem делает.

Тогда нужно ли инициализировать память после вызова GetMem? Док говорит да. Но я вижу в некоторых исходных кодах Delphi, что они не вызывают Initialize.

И нужно ли дорабатывать память после окончания использования? Я вижу в некоторых исходных кодах Delphi, что они делают, но что-то не делают.

Спасибо

Какой-то странный вопрос, учитывая, что вы даете ответ в тексте вашего вопроса.

David Heffernan 21.05.2019 09:44
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
1 436
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

Логика кажется простой — если вам нужен буфер с нулевой инициализацией, вы можете использовать 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?

alancc 21.05.2019 12:11

Почему вы чувствуете потребность вызвать Finalize? Что именно нужно доработать? Для массивов вы используете динамические массивы, и они финализируются автоматически. Если вам нужно использовать динамическое размещение для записей, вы используете New и Dispose и таким образом завершаете их. Почему ты вообще звонишь GetMem или AllocMem? Никто не может предложить руководство без четкого понимания контекста?

David Heffernan 21.05.2019 12:13

Меня не очень волнует этот ответ из-за заключительной части. Любая память, выделенная вручную, должна быть освобождена вручную. Вы говорите, что «исключений мало», но это неправда. Исключений нет. Если вы выделяете с помощью GetMem или AllocMem, то вы должны называть FreeMem. Если вы выделяете с помощью New, вы должны вызвать Dispose. Элементы с подсчетом ссылок, такие как строки, динамические массивы, интерфейсы, анонимные методы и т. д., не выделяются с помощью GetMem, AllocMem или New. Что касается передачи права собственности, то это несколько ортогонально. Это просто делегирует ответственность.

David Heffernan 21.05.2019 12:25

@DavidHeffernan В прошлой части я говорил в более общем смысле, чем только AllocMem или GetMem.

Charlie 21.05.2019 12:31

Однако это не совсем ясно, и вопрос явно заключается в ручном выделении памяти. Возможно, некоторая неясность на самом деле является результатом вопроса о завершении. Я не думаю, что спрашивающий имеет в виду освобождение, когда говорит завершение. Я думаю, что он имеет в виду финализацию с точки зрения управляемых типов. Но если мы собираемся говорить об управляемых типах, то это должно быть явно указано в вопросе, и, кроме того, управляемые типы не должны выделяться с помощью GetMem или AllocMem.

David Heffernan 21.05.2019 12:38

Другие вопросы по теме