2009-05-14 17 views
29

Así que siempre escuché que los campos de clase (basados ​​en el montón) se inicializaban, pero las variables basadas en la pila no. También escuché que los miembros del registro (que también están basados ​​en la pila) tampoco se inicializaron. El compilador advierte que las variables locales no se inicializan ([Advertencia de DCC] W1036 La variable 'x' podría no haberse inicializado), pero no advierte para miembros de registro. Entonces decidí hacer una prueba.¿Qué variables se inicializan cuando está en Delphi?

siempre consigo de enteros y falsa de Booleanos para todos los miembros de discos.

Intenté activar y desactivar varias opciones de compilación (depuración, optimizaciones, etc.), pero no hubo diferencia. Todos mis miembros de registro están siendo inicializados.

¿Qué me estoy perdiendo? Estoy en Delphi 2009 Actualización 2.

program TestInitialization; 

{$APPTYPE CONSOLE} 

uses 
    SysUtils; 

type 
    TR = Record 
    Public 
    i1, i2, i3, i4, i5: Integer; 
    a: array[0..10] of Integer; 
    b1, b2, b3, b4, b5: Boolean; 
    s: String; 
    End; 

var 
    r: TR; 
    x: Integer; 

begin 
    try 
    WriteLn('Testing record. . . .'); 
    WriteLn('i1 ',R.i1); 
    WriteLn('i2 ',R.i2); 
    WriteLn('i3 ',R.i3); 
    WriteLn('i4 ',R.i4); 
    WriteLn('i5 ',R.i5); 

    Writeln('S ',R.s); 

    Writeln('Booleans: ', R.b1, ' ', R.b2, ' ', R.b3, ' ', R.b4, ' ', R.b5); 

    Writeln('Array '); 
    for x := 0 to 10 do 
     Write(R.a[x], ' '); 
    WriteLn; 

    WriteLn('Done . . . .'); 
    except 
    on E:Exception do 
     Writeln(E.Classname, ': ', E.Message); 
    end; 
    ReadLn; 
end. 

Salida:

 
Testing record. . . . 
i1 0 
i2 0 
i3 0 
i4 0 
i5 0 
S 
Booleans: FALSE FALSE FALSE FALSE FALSE 
Array 
0 0 0 0 0 0 0 0 0 0 0 
Done . . . . 

+0

ver también: http://stackoverflow.com/questions/132725/are-delphi-variables-initialized-with-a-value-by-default – Ampere

Respuesta

42

variables globales se inicializan-cero. Las variables utilizadas en el contexto del principal begin .. end bloque de un programa pueden ser un caso especial; a veces se tratan como variables locales, particularmente for -indicadores de bucle. Sin embargo, en su ejemplo, r es una variable global y se asigna desde la sección .bss del archivo ejecutable, que el cargador de Windows garantiza que no se completa.

Las variables locales se inicializan como si se pasaran a la rutina Initialize. La rutina Initialize utiliza runtime type-info (RTTI) para cero campos (recursivamente, si un campo es de una matriz o tipo de registro) y arrays (recursivamente, si el tipo de elemento es una matriz o un registro) de un tipo administrado , donde un tipo administrado es uno de:

  • AnsiString
  • UnicodeString
  • WideString
  • un tipo de interfaz (incluyendo referencias de métodos)
  • dinámica tipo de matriz
  • Variant

Las asignaciones del montón no se inicializan necesariamente; depende de qué mecanismo se utilizó para asignar memoria. Las asignaciones como parte de los datos de objeto de instancia se llenan con cero por TObject.InitInstance. Las asignaciones desde AllocMem están llenas a cero, mientras que las asignaciones GetMem no están llenas a cero. Las asignaciones desde New se inicializan como si se pasasen a Initialize.

+1

Lo importante es recordar que "inicializado" <> "lleno de cero". Por ejemplo, el registro inicializado con los campos de cadena y enteros no se puede completar a cero. Por supuesto, el campo de cadena será nulo, pero el campo entero puede ser <> 0. – Alex

+1

Sí - Initialize inicializa los campos y los elementos de la matriz solo de tipos administrados. –

+1

métodos anónimos deben agregarse a la lista Creo –

1

que tienen una situación similar, y pensaba lo mismo, pero cuando agrego otras variables utilizadas antes del registro, los valores convertido en basura, por lo que antes de que yo uso mi disco que tenía para inicializar utilizando

FillChar(MyRecord, SizeOf(MyRecord), #0) 
+0

He oído que FillChar puede causar problemas si su registro contiene miembros administrados previamente asignados (cadenas, etc.) o referencias de objetos asignados, pero para registros nuevos está en lo correcto. –

+2

@Jim: Allen respondió una pregunta acerca de que, hace unos días, le dijo a FillChar que no afectará cuando se use solo para la inicialización, pero después de acceder a un miembro de recuento y luego llamar a fillchar, obtendrá una pérdida de memoria. –

+1

Puede llamar a 'Finalize' en el registro antes de' FillChar'. Eso asegurará que todos los campos contados de referencia se limpiarán primero. –

1

Tenga en cuenta que en el código de ejemplo que proporcionó, el registro es en realidad una variable global, por lo que se inicializará por completo.Si mueve todo ese código a una función, será una variable local, y así, de acuerdo con las reglas dadas por Barry Kelly, solo su campo de cadena se inicializará (a '').

+0

Tiene razón, lo intenté después de leer la respuesta de Barry. –

6

Siempre obtengo 0 de enteros y falso de booleanos para todos los miembros del registro.

Intenté activar y desactivar varias opciones de compilación (depuración, optimizaciones, etc.), pero no hubo diferencia. Todos mis miembros de registro están siendo inicializados.

¿Qué me estoy perdiendo?

Bueno, aparte de su prueba usando global en vez de variables locales: la cosa importante que se echa en falta es la distinción entre variables que aparecieron por coincidencia a ser inicializado, y variables que actally son initialised.
BTW: Esta es la razón por la cual los programadores que no revisan sus advertencias cometen el error común de suponer que su código mal escrito se comporta correctamente cuando hacen algunas pruebas; sucede que tiene 0 y los valores falsos .... Want To Buy: random initialisation of local variables for debug builds.

Considere la siguiente variación en su código de prueba:

program LocalVarInit; 

{$APPTYPE CONSOLE} 

procedure DoTest; 
var 
    I, J, K, L, M, N: Integer; 
    S: string; 
begin 
    Writeln('Test default values'); 
    Writeln('Numbers: ', I:10, J:10, K:10, L:10, M:10, N:10); 
    Writeln('S: ', S); 
    I := I + 1; 
    J := J + 2; 
    K := K + 3; 
    L := L + 5; 
    M := M + 8; 
    N := N + 13; 
    S := 'Hello'; 
    Writeln('Test modified values'); 
    Writeln('Numbers: ', I:10, J:10, K:10, L:10, M:10, N:10); 
    Writeln('S: ', S); 
    Writeln(''); 
    Writeln(''); 
end; 

begin 
    DoTest; 
    DoTest; 
    Readln; 
end. 

Con la salida de ejemplo siguiente:

Test default values 
Numbers: 4212344 1638280 4239640 4239632   0   0 
S: 
Test modified values 
Numbers: 4212345 1638282 4239643 4239637   8  13 //Local vars on stack at end of first call to DoTest 
S: Hello 


Test default values 
Numbers: 4212345 1638282 4239643 4239637   8  13 //And the values are still there on the next call 
S: 
Test modified values 
Numbers: 4212346 1638284 4239646 4239642  16  26 
S: Hello 

Notas

  • El ejemplo funciona mejor si compila ingenio h optimización desactivada. De lo contrario, si tiene la optimización activada:
    • Algunos valores locales se manipularán en los registros de la CPU.
    • Y si ve la pila de la CPU al recorrer el código, notará por ejemplo que I := I + 1 ni siquiera modifica la pila. Entonces, obviamente, el cambio no puede llevarse a cabo.
  • Puede experimentar con diferentes convenciones de llamadas para ver cómo eso afecta las cosas.
  • También puede probar el efecto de establecer los valores locales en cero en lugar de incrementarlos.
  • Esto ilustra cómo usted es completamente dependiente de lo que encontró en la pila antes de llamar al método.
+3

"los programadores que no revisan sus advertencias cometen el error común" - Leí en alguna parte un consejo para los programadores de Delphi y desde entonces hice que mi consejo fuera mi lema: "¡CONSIDERA CONSEJOS PARA EL COMPILADOR COMO ADVERTENCIA Y LAS ADVERTENCIAS COMO ERRORES"! – Ampere

+2

@Altar Daría un paso más y seguiré una política de consejos y advertencias cero. Tan pronto como comienzas a hacer alguna excusa para permitir algunas pistas, creas la posibilidad de que te pierdas algunas nuevas entre cientos de las anteriores. Cualquier sugerencia/advertencia puede ser fácilmente reparada/evitada, pero se necesita mucho trabajo aburrido para arreglar cientos de ellas. –

+0

Sí. Ese es mi punto: "política de consejos y advertencias cero" :) – Ampere

Cuestiones relacionadas