views:

367

answers:

4

I have a lot of memory allocations and the same number of FreeMem calls. What I didn't have though is a check before calling freemem to see if the pointer was nil, and a line after freeing to set the pointer to nil.

I tried to create a function to do this

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;

But there's a problem. It can't set the origional pointer to nil because the parameter isn't variable (var p: Pointer). I can't use var though because if I do the compiler complains the type has to be the exact same type (Pointer). The pointers I'm passing could be pointers to any type (PChar, regular pointer, etc.).

What can I do to fix this? Is there a better solution?

+8  A: 

There's a procedure in SysUtils called FreeAndNil that does this for objects. It does it by using an untyped var parameter which it casts to TObject, and it's up to you to ensure you don't pass it something that's not a TObject. You could do something similar here if you needed to. Just be careful; there's no type safety if you do that.

Mason Wheeler
Thanks for such a quick response, I wish there were a way I could say multiple answers were correct.
Daisetsu
No, you have to select only one, but you can change during alimited time frame.
Uwe Raabe
+5  A: 

Like Mason Wheeler said you should use the same trick as FreeAndNil in the SysUtils unit does on object references.
So I modified your code, unit tested it, and this works fine:

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: Rob Kennedy wrote a nice answer on untyped var parameters that has a link to his untyped parameter page on the internet.

PS2: For reference: The Kylix version of SysUtils.pas is on-line, and the FreeAndNil there is identical to how it is in Delphi.

Jeroen Pluimers
Of course if you use that Kylix code under GPL, and don't have a Kylix license, now your whole app is GPL.
Marco van de Voort
The code shown above is *not* `FreeAndNil`. But I would not use this code anyway; I would rather use the code of Rob Kennedy.
Andreas Rejbrand
@Andreas: it indeed is not; the source by Rob Kennedy does not have a `size` parameter as Daisetu asked, and uses a slightly different order of nilling and freeing.
Jeroen Pluimers
@Marco: that Kylix link is meant as reference, I did not use it to modify the code from Daisetu (and yes: I do have Kylix licenses)
Jeroen Pluimers
@Jeroen: I know you do (or that as vendor that is extremely likely), just wanted to warn the general public to avoid being too liberal with public Kylix code. Better take it from Free Pascal, it's license is more liberal. For elementary stuff like this, it will work fine.
Marco van de Voort
+11  A: 

To be able to pass arbitrary pointer values to that function, you need to follow the same model as FreeAndNil and pass in an untyped parameter. Otherwise, the compiler correctly complains about actual and formal parameter types not being identical. Type-cast the untyped parameter to Pointer when you call FreeMem on it.

You're doing a couple of pointless things in that function.

First of all is that freeing a nil pointer is always safe, so there's no reason to check for that before calling FreeMem. It's freeing a non-nil pointer that you need to worry about, but no function can protect you from that.

Next, the size parameter to FreeMem has been ignored for many years. It used to be that if you provided that parameter, it needed to match the size passed to GetMem, but nowadays, FreeMem completely ignores that parameter — the compiler doesn't even pass that parameter to the function.

With all of the above in mind, your function boils down to this:

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

Be careful not to accidentally call that function on anything that isn't a pointer allocated with GetMem. The compiler won't catch it for you like it could if you were using typed parameters. If you attempt to free something that wasn't allocated with GetMem, you'll probably get an EInvalidPointer exception, but the variable you passed in will still be nil afterward. That's the same way FreeAndNil works.

Rob Kennedy
I am still using Delphi 6 which has quite an old compiler. If I free a nil pointer I get an error, so I do need to check first. Are you sure that I don't need the second parameter to FreeMem even though I'm using a very old compiler?
Daisetsu
You have mis-diagnosed the problem. Freeing a nil pointer has always been safe. Your problem lies elsewhere.
Rob Kennedy
@Daisetsu - I have just tried this on D6 and it works fine with a nil ptr. `P := nil; FreeMem(P);` gives no errors. An unassigned pointer may be pointing to garbage though.
Gerry
@Rob Kennedy Thanks Rob, guess I was wrong about that. Sorry didn't choose your answer as the "correct" one, I wish I could change my selection now. I appreciate your help.
Daisetsu
My understanding of Stack Overflow is that you can change your selection whenever you want.
Rob Kennedy
+2  A: 

I tend to work with ReallocMem a lot for pointer/memory operation.

Calling

ReallocMem(P,0)

will set the pointer to Nil.

1 thing you need to know about using it, P needs to be initialized before being passed to ReallocMem.

Ken Bourassa
+1; now that is new to me (seems 25 years of Turbo Pascal experience leaves plenty of room to learn old things <g>).
Jeroen Pluimers