Свободная память и ноль в Delphi с использованием одной функции

У меня много выделений памяти и такое же количество вызовов 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;

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

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

4 ответов


Как Мейсон Уилер сказал, что вы должны использовать тот же трюк, что 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;

--jeroen

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

PS2: для справки:Kylix версия SysUtils.pas on-line, и FreeAndNil там идентичен тому, как он находится в Delphi.


чтобы иметь возможность передавать произвольные значения указателя этой функции, вам нужно следовать той же модели, что и FreeAndNil, и передать нетипизированный параметр. В противном случае компилятор правильно жалуется на то, что фактические и формальные типы параметров не идентичны. Type-приведите нетипизированный параметр к указателю при вызове 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 работает.


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


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

вызов

ReallocMem(P,0)

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

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