¿Por qué la mayoría de los ejemplos de Delphi utilizan FillChar () para inicializar los registros?

StackOverflow https://stackoverflow.com/questions/783825

  •  13-09-2019
  •  | 
  •  

Pregunta

Me preguntaba, ¿por qué ejemplos más Delphi utilizan FillChar () para inicializar los registros.

type
  TFoo = record
    i: Integer;
    s: string; // not safe in record, better use PChar instead
  end;

const
  EmptyFoo: TFoo = (i: 0; s: '');

procedure Test;
var
  Foo: TFoo;
  s2: string;
begin
  Foo := EmptyFoo; // initialize a record

  // Danger code starts
  FillChar(Foo, SizeOf(Foo), #0);
  s2 := Copy("Leak Test", 1, MaxInt); // The refcount of the string buffer = 1
  Foo.s = s2; // The refcount of s2 = 2
  FillChar(Foo, SizeOf(Foo), #0); // The refcount is expected to be 1, but it is still 2
end;
// After exiting the procedure, the string buffer still has 1 reference. This string buffer is regarded as a memory leak.

Aquí ( http: //stanleyxu2005.blogspot. com / 2008/01 / potencial de memoria de fugas por initializing.html ) es mi nota sobre este tema. OMI, declarar una constante con un valor por defecto es una mejor manera.

¿Fue útil?

Solución

razones históricas, en su mayoría. FillChar () se remonta a los días de Turbo Pascal y fue utilizado para tales fines. El nombre es en realidad un nombre poco apropiado ya que si bien dice Rellena Char (), que es realmente Rellena Byte (). La razón es que el último parámetro puede tener un char o un byte. Así FillChar (Foo, sizeof (Foo), # 0) y FillChar (Foo, sizeof (Foo), 0) son equivalentes. Otra fuente de confusión es que a partir de Delphi 2009, FillChar todavía sólo llena bytes a pesar de que Char es equivalente a WideChar. Mientras mira a los usos más comunes de FillChar con el fin de determinar si la mayoría de la gente utiliza FillChar para llenar realidad de memoria con datos de caracteres o sólo lo utilizan para inicializar la memoria con algún valor de byte dado, se encontró que era el último caso que dominó su uso en lugar de la antigua. Con eso decidimos mantener FillChar byte-céntrica.

Es cierto que la limpieza de un registro con FillChar que contiene un campo declarado usando uno de los tipos "gestionados" (cadenas, variante, interfaz, matrices dinámicas) puede ser peligroso si no se utiliza en el contexto adecuado. En el ejemplo que dio, sin embargo, en realidad es seguro llamar FillChar en la variable de registro declarado localmente con tal de que es el primero que alguna vez has hecho al registro dentro de ese ámbito . La razón es que el compilador ha generado código para inicializar el campo de cadena en el registro. Esto ya se ha establecido el campo de cadena a 0 (cero). Llamando FillChar (Foo, sizeof (Foo), 0) se acaba de sobrescribir todo el disco con 0 bytes, incluyendo el campo de cadena que ya es 0. Usando FillChar en la variable de registro después un valor fue asignado a el campo de cadena, no se recomienda. Usando su técnica constante inicializado es una muy buena solución a este problema debido a que el compilador puede generar el código apropiado para asegurar los valores de los registros existentes están debidamente finalizados durante la asignación.

Otros consejos

Si tiene Delphi 2009 y, posteriormente, utilizar la llamada Default para inicializar un registro.

Foo := Default(TFoo); 

la respuesta de David a la pregunta Como los registros que contienen varios tipos de Delphi a la vez adecuadamente libre? .

Editar:

La ventaja de usar la llamada Default(TSomeType), es que el registro esté finalizado antes de que se haga efectivo. No hay pérdidas de memoria y sin explícita peligrosa llamada de bajo nivel para FillChar o ZeroMem. Cuando los registros son complejas, tal vez contiene registros anidados, etc., el riesgo de cometer errores es eliminado.

Su método para inicializar los registros se puede hacer aún más sencilla:

const EmptyFoo : TFoo = ();
...
Foo := EmptyFoo; // Initialize Foo

A veces desea un parámetro que tiene un valor no predeterminado, y luego hacerlo de esta manera:

const PresetFoo : TFoo = (s : 'Non-Default'); // Only s has a non-default value

Esto ahorrará algo de tecleo y el foco se establece en las cosas importantes.

FillChar está bien para asegurarse de que no recibe ninguna basura en un nueva estructura, sin inicializar (registro, tampón, arrray ...).
No debe usarse para "restablecer" los valores sin saber lo que su está restableciendo.
No hay más que escribir MyObject := nil y esperando para evitar una pérdida de memoria.
En particulart todos los tipos administrados deben ser observado cuidadosamente.
Véase Finalizar función.

Cuando se tiene el poder para violín directamente con la memoria, siempre hay una manera de disparar en el pie.

FillChar se utiliza generalmente para llenar Arrays o registros con sólo tipos numéricos y matriz. Tiene razón en que no se debe utilizar para cuando hay cadenas (o cualquier variable Ref-contado) en el registro .

A pesar de su sugerencia de utilizar un const para inicializar que iba a funcionar, un problema entra en juego cuando tengo un longitud variable array que Quiero inicializar.

La pregunta también puede preguntar:

No hay Función ZeroMemory en Windows. En los archivos de cabecera (winbase.h) es una macro que, en el mundo C, se da la vuelta y llama memset:

memset(Destination, 0, Length);

ZeroMemory es el término independiente del idioma para "función de su plataforma que puede ser utilizada para la memoria cero"

El equivalente Delphi de memset es FillChar.

Desde Delphi no tiene macros (y antes de los días de procesos en línea), llamando ZeroMemory significaba que tenía que sufrir la pena de una llamada a una función adicional antes de que realmente tiene que FillChar .

Así que de muchas maneras, llamando a FillChar es una actuación de micro-optimización - que ya no existe ahora que ZeroMemory se colocarán en línea:

procedure ZeroMemory(Destination: Pointer; Length: NativeUInt); inline;

Bonus Reading

Windows también contiene el SecureZeroMemory función. Lo hace exactamente lo mismo que ZeroMemory . Si lo hace lo mismo que ZeroMemory , ¿por qué existe?

Debido a que algunos C inteligente / compiladores de C ++ podría reconocer que la fijación de la memoria a 0 antes de deshacerse de la memoria es una pérdida de tiempo -. De distancia y optimizar la llamada a ZeroMemory

No creo compilador de Delphi es tan inteligente como muchos otros compiladores; así que no hay necesidad de un SecureFillChar .

Tradicionalmente, un personaje es un solo byte (ya no es cierto para Delphi 2009), por lo que usar FILLCHAR con un # 0 que inicializar la memoria asignada de forma que sólo contenía nulos, o byte 0, o bin 00000000.

Se debe utilizar en su lugar el ZeroMemory función de la compatibilidad, que tiene los mismos parámetros de llamada como el viejo FILLCHAR.

Esta pregunta tiene una implicación más amplia que ha estado en mi mente durante mucho tiempo. Yo también, me crié en el uso de FillChar para los registros. Esto es bueno porque a menudo añadimos nuevos campos para el registro (datos) y por supuesto FillChar (Rec, sizeof (Rec), # 0) se encarga de estos nuevos campos. Si somos hacerlo correctamente ', tenemos que iterar a través de todos los campos del registro, algunos de los cuales se enumeran los tipos, algunos de los cuales pueden ser registros ellos mismos y el código resultante es menos legible, así sea posiblemente errónea si nosotros no añadir nueva campos de registro de forma diligente. campos de cadena son comunes, por lo tanto FillChar es un no-no ahora. Hace unos meses, fui alrededor y convertido todos mis FillChars en los registros con campos de cadena a una compensación iterativa, pero no estaba contento con la solución y pregunto si hay una clara forma de hacer el 'Fill' en simplemente tipos (ordinal / flotar) y 'Finalizar' en las variantes y las cadenas?

Esta es una mejor manera de inicializar cosas sin usar FillChar:

registro en el registro (No se puede inicializar)
Cómo inicializar una matriz estática?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top