Свободная память и ноль в Delphi с помощью одной функции - PullRequest
8 голосов
/ 11 августа 2010

У меня много выделений памяти и столько же вызовов FreeMem. Однако у меня не было проверки перед вызовом freemem, чтобы увидеть, был ли указатель нулем, и строки после освобождения, чтобы установить указатель на ноль.

Я пытался создать функцию для этого

procedure FreeMemAndNil(p: Pointer; size: Integer = -1);
begin
  if p <> nil then
  begin
    if size > -1 then
      FreeMem(p, size)
    else
      FreeMem(p);
    p := nil;
  end;
end;

Но есть проблема. Он не может установить исходный указатель на nil, потому что параметр не является переменным (var p: Pointer). Я не могу использовать var, потому что, если я это сделаю, компилятор жалуется, что тип должен быть точно такого же типа (указатель). Указатели, которые я передаю, могут быть указателями на любой тип (PChar, обычный указатель и т. Д.).

Что я могу сделать, чтобы это исправить? Есть ли лучшее решение?

Ответы [ 4 ]

13 голосов
/ 11 августа 2010

Чтобы иметь возможность передавать произвольные значения указателя в эту функцию, вам нужно следовать той же модели, что и FreeAndNil, и передавать нетипизированный параметр .В противном случае компилятор правильно жалуется на то, что фактические и формальные типы параметров не идентичны.Приведите тип нетипизированного параметра к Pointer, когда вы вызываете FreeMem для него.

Вы делаете пару бессмысленных действий в этой функции.

Прежде всего, это освобождение нулевого указателя.всегда безопасно, поэтому нет причин проверять это перед вызовом FreeMem.Это освобождение не-нулевого указателя, о котором вам нужно беспокоиться, но никакая функция не может защитить вас от этого.

Далее, параметр размера для FreeMem игнорировался в течение многих лет.Раньше считалось, что если вы предоставили этот параметр, он должен был соответствовать размеру, переданному в GetMem, но в настоящее время FreeMem полностью игнорирует этот параметр - компилятор даже не передает этот параметр в функцию.

СИмея в виду все вышесказанное, ваша функция сводится к следующему:

procedure FreeMemAndNil(var P);
var
  Tmp: Pointer;
begin
  Tmp := Pointer(P);
  Pointer(P) := nil;
  FreeMem(Tmp);
end;

Будьте осторожны, чтобы случайно не вызвать эту функцию для всего, что не является указателем, выделенным с помощью GetMem.Компилятор не поймает его за вас, как если бы вы использовали типизированные параметры.Если вы попытаетесь освободить то, что не было выделено с помощью GetMem, вы, вероятно, получите исключение EInvalidPointer, но переданная вами переменная все равно будет равна нулю.То же самое работает FreeAndNil.

9 голосов
/ 11 августа 2010

В SysUtils есть процедура FreeAndNil, которая делает это для объектов.Он делает это с помощью нетипизированного параметра var , который он приводит к TObject, и вы должны убедиться, что вы не передали ему что-то, что не является TObject.Вы можете сделать что-то подобное здесь, если вам нужно.Просто будь осторожен;если ты это сделаешь, безопасность типов не будет.

6 голосов
/ 11 августа 2010

Как и Мейсон Уилер сказал, что вы должны использовать тот же трюк, что и FreeAndNil в модуле SysUtils для ссылок на объекты.
Поэтому я изменил ваш код, проверил его модулем, и это прекрасно работает:

procedure FreeMemAndNil(var ptr; size: Integer = -1);
var
  p: Pointer;
begin
  p := Pointer(ptr);
  if p <> nil then
  begin
    if size > -1 then
      FreeMem(p, size)
    else
      FreeMem(p);
    Pointer(ptr) := nil;
  end;
end;

- Йерун

PS: Роб Кеннеди написал хороший ответ на нетипизированные параметры var , в котором есть ссылка на его страницу нетипизированных параметров в Интернете.

PS2: Для справки: Версия SysUtils.pas для Kylix находится в режиме онлайн , а FreeAndNil там идентичен версии в Delphi.

4 голосов
/ 12 августа 2010

Я склонен много работать с ReallocMem для работы с указателем / памятью.

Вызов

ReallocMem(P,0)

установит указатель на ноль.

1 вещь, которая вам нужнаЧтобы узнать, как его использовать, P необходимо инициализировать перед передачей в ReallocMem.

...