2010-08-11 21 views
8

Tengo muchas asignaciones de memoria y el mismo número de llamadas de FreeMem. Sin embargo, lo que no tenía es una comprobación antes de llamar a freemem para ver si el puntero era nulo, y una línea después de liberar para establecer el puntero a cero.Memoria libre y nula en Delphi usando una sola función

Me trataron de crear una función para hacer esto

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; 

Pero hay un problema. No puede establecer el puntero original en nil porque el parámetro no es variable (var p: Pointer). No puedo usar var aunque porque si el compilador se queja, el tipo tiene que ser del mismo tipo (Puntero). Los punteros que estoy pasando podrían ser punteros a cualquier tipo (PChar, puntero regular, etc.).

¿Qué puedo hacer para solucionarlo? ¿Hay una mejor solución?

+0

there' FreeAndNil' http://www.delphibasics.co.uk/RTL.asp?Name=FreeAndNil – Andrey

+0

@Andrey FreeAndNil solo funciona con objetos. Estoy lidiando con la memoria que asigné manualmente a un puntero. Gracias por su aporte. – Daisetsu

Respuesta

6

Me gusta Mason Wheeler dijo que debe utilizar el mismo truco como FreeAndNil en la unidad SysUtils hace referencia a objetos.
Así que he modificado el código, la unidad probada, y esto funciona bien:

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 escribió una respuesta agradable en untyped var parameters que tiene un enlace a su página de parámetros sin tipo En Internet.

PS2: Para referencia: The Kylix version of SysUtils.pas is on-line, y el FreeAndNil no es idéntico a como está en Delphi.

+1

Por supuesto, si usa ese código de Kylix en GPL y no tiene una licencia de Kylix, ahora toda su aplicación es GPL. –

+1

El código que se muestra arriba es * no * 'FreeAndNil'. Pero no usaría este código de todos modos; Prefiero usar el código de Rob Kennedy. –

+0

@Andreas: de hecho no lo es; la fuente de Rob Kennedy no tiene un parámetro de "tamaño" como Daisetu preguntó, y utiliza un orden ligeramente diferente de nilling y freeing. –

9

Hay un procedimiento en SysUtils llamado FreeAndNil que hace esto para los objetos. Lo hace mediante el uso de un parámetro var var que se envía a TObject, y depende de usted asegurarse de que no se le pase algo que no sea un TObject. Podría hacer algo similar aquí si lo necesitara. Sólo sé cuidadoso; no hay seguridad de tipo si haces eso.

+0

Gracias por una respuesta tan rápida, ojalá hubiera una manera en que pudiera decir que las respuestas múltiples eran correctas. – Daisetsu

+0

No, debe seleccionar solo una, pero puede cambiar durante un período de tiempo limitado. –

13

Para poder pasar valores de puntero arbitrarios a esa función, debe seguir el mismo modelo que FreeAndNil y pasar un parámetro sin tipo. De lo contrario, el compilador se queja correctamente de que los tipos de parámetros reales y formales no son idénticos. Escriba el parámetro sin tipo en Pointer cuando llame a FreeMem en él.

Estás haciendo un par de cosas sin sentido en esa función.

Lo primero de todo es que liberar un puntero nulo siempre es seguro, por lo que no hay ninguna razón para comprobarlo antes de llamar a FreeMem. Está liberando un puntero no nulo del que debe preocuparse, pero ninguna función puede protegerlo de eso.

A continuación, el parámetro de tamaño para FreeMem se ha ignorado durante muchos años. Solía ​​ser que si proporcionaba ese parámetro, tenía que coincidir con el tamaño pasado a GetMem, pero hoy en día, FreeMem ignora por completo ese parámetro, el compilador ni siquiera pasa ese parámetro a la función.

Con todo lo anterior en mente, su función se reduce a esto:

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

Tenga cuidado de no llamar accidentalmente esa función en cualquier cosa que no es un puntero asignado con GetMem. El compilador no lo captará como lo haría si estuviera usando parámetros escritos.Si intentas liberar algo que no fue asignado con GetMem, probablemente obtendrás una excepción EInvalidPointer, pero la variable que ingresaste seguirá siendo nula después. Así es como funciona FreeAndNil.

+0

Todavía estoy usando Delphi 6, que tiene un compilador bastante antiguo. Si libero un puntero nulo, aparece un error, así que necesito verificar primero. ¿Estás seguro de que no necesito el segundo parámetro para FreeMem aunque estoy usando un compilador muy antiguo? – Daisetsu

+1

Ha diagnosticado mal el problema. Liberar un puntero nulo siempre ha sido seguro. Tu problema está en otra parte. –

+0

@Daisetsu - Acabo de probar esto en D6 y funciona bien con un ptr nulo. 'P: = nil; FreeMem (P); 'no da ningún error. Sin embargo, un puntero no asignado puede estar apuntando a la basura. –

4

Tiendo a trabajar mucho con ReallocMem para el funcionamiento del puntero/memoria.

Calling

ReallocMem(P,0) 

establecerá el puntero a Nada.

Lo que necesita saber sobre su uso, P debe inicializarse antes de pasarlo a ReallocMem.

+0

+1; ahora eso es nuevo para mí (parece que la experiencia de 25 años de Turbo Pascal deja mucho espacio para aprender cosas viejas ). –

+0

Sí, buena idea usar mal RealllocMem para desasignar. – TheBlastOne

Cuestiones relacionadas