¿Qué variables se inicializan cuando está en Delphi?

Así que siempre escuché que los campos de clase (basados ​​en el montón) se inicializaban, pero las variables basadas en la stack no. También escuché que los miembros del registro (que también están basados ​​en la stack) tampoco se inicializaron. El comstackdor 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.

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

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

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

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 ',Rs); Writeln('Booleans: ', R.b1, ' ', R.b2, ' ', R.b3, ' ', R.b4, ' ', R.b5); Writeln('Array '); for x := 0 to 10 do Write(Ra[x], ' '); WriteLn; WriteLn('Done . . . .'); except on E:Exception do Writeln(E.Classname, ': ', E.Message); end; ReadLn; end. 

Salida:

 Registro de prueba.  .  .  .
 I 10
 i2 0
 i3 0
 i4 0
 i5 0
 S
 Booleanos: FALSO FALSO FALSO FALSO FALSO
 Formación
 0 0 0 0 0 0 0 0 0 0 0
 Hecho .  .  .  .

Las variables globales tienen cero inicialización. Las variables utilizadas en el contexto del bloque end principal … de un progtwig pueden ser un caso especial; a veces se tratan como variables locales, particularmente for indexadores 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 poner a 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 gestionado , donde un tipo administrado es uno de:

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

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 mediante TObject.InitInstance . Las asignaciones de AllocMem están AllocMem cero, mientras que GetMem asignaciones de GetMem no se llenan a cero. Las asignaciones de New se inicializan como si se pasaran a Initialize .

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

Intenté activar y desactivar varias opciones de comstackció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, usa variables globales en lugar de locales: lo importante que se está perdiendo es la distinción entre las variables que coincidentemente parecen haberse inicializado, y las variables que se inicializan de manera actal .
Por cierto : esta es la razón por la cual los progtwigdores que no revisan sus advertencias cometen el error común de asumir que su código mal escrito se está comportando correctamente cuando hacen pocas pruebas; sucede que tiene 0 y valores predeterminados 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 el siguiente resultado de muestra:

 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 comstack con la optimización desactivada. De lo contrario, si tiene la optimización activada:
    • Algunos vars locales serán manipulados en registros de CPU.
    • Y si ves la stack de la CPU al pasar por el código, notarás, por ejemplo, que I := I + 1 ni siquiera modifica la stack. 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 dependes totalmente de lo que encontró en la stack antes de que se llamara a tu método.

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í, según las reglas dadas por Barry Kelly, solo se inicializará su campo de cadena (a ”).

Tengo una situación similar, y pensé lo mismo, pero cuando agrego otras variables utilizadas antes del registro, los valores se vuelven basura, así que antes de usar mi registro tuve que inicializar usando

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