Quais variables ​​são inicializadas quando no Delphi?

Então eu sempre ouvi que os campos de class (baseados em heap) foram inicializados, mas as variables ​​baseadas em pilha não foram. Também ouvi dizer que os membros do registro (também baseados em pilha) também não foram inicializados. O compilador avisa que as variables ​​locais não foram inicializadas ([Aviso do DCC] A variável W1036 ‘x’ pode não ter sido inicializada), mas não avisa para os membros do registro. Então eu decidi fazer um teste.

Eu sempre recebo 0 de Integers e false de Booleans para todos os membros de registro.

Eu tentei ativar e desativar várias opções de compilador (debugging, otimizações, etc.), mas não houve diferença. Todos os meus membros de registro estão sendo inicializados.

o que estou perdendo? Eu estou no Delphi 2009 Update 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 ',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. 

Saída:

 Registro de teste.  .  .  .
 i1 0
 i2 0
 i3 0
 i4 0
 i5 0
 S
 Booleans: FALSE FALSE FALSE FALSE FALSE
 Matriz
 0 0 0 0 0 0 0 0 0 0 0
 Feito .  .  .  .

Variáveis ​​globais são inicializadas com zero. As variables ​​usadas no contexto do bloco principal begin .. end de um programa podem ser um caso especial; às vezes eles são tratados como variables ​​locais, particularmente for indexadores de loop. No entanto, no seu exemplo, r é uma variável global e alocada a partir da seção .bss do executável, que o carregador do Windows garante que seja preenchido com zero.

Variáveis ​​locais são inicializadas como se fossem passadas para a rotina Initialize . A rotina Initialize usa RTTI (tempo de execução) para campos de zerar (recursivamente – se um campo é de uma matriz ou tipo de registro) e matrizes (recursivamente – se o tipo de elemento for uma matriz ou um registro) de um tipo gerenciado , onde um tipo gerenciado é um dos seguintes:

  • AnsiString
  • UnicodeString
  • WideString
  • um tipo de interface (incluindo referências de método)
  • tipo de matriz dinâmica
  • Variante

Alocações do heap não são necessariamente inicializadas; isso depende de qual mecanismo foi usado para alocar memory. As alocações como parte dos dados do object da instância são preenchidas com zero pelo TObject.InitInstance . As alocações de AllocMem são preenchidas com zero, enquanto GetMem alocações de GetMem não são preenchidas com zero. As alocações de New são inicializadas como se fossem passadas para Initialize .

Eu sempre recebo 0 de Integers e false de Booleans para todos os membros de registro.

Eu tentei ativar e desativar várias opções de compilador (debugging, otimizações, etc.), mas não houve diferença. Todos os meus membros de registro estão sendo inicializados.

o que estou perdendo?

Bem, além de seu teste usando variables ​​globais em vez de locais: a coisa importante que está faltando é a distinção entre variables ​​que coincidentemente parecem ser inicializadas e variables ​​que são inicializadas.
BTW : Esta é a razão pela qual os programadores que não checam seus avisos cometem o erro comum de assumir que seu código mal escrito está se comportando corretamente quando os poucos testes que eles fazem; Por acaso, tem 0 e falsos padrões …. Want To Buy: random initialisation of local variables for debug builds.

Considere a seguinte variação no seu código de teste:

 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. 

Com o seguinte exemplo de saída:

 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

  • O exemplo funciona melhor se você compilar com a otimização desativada. Caso contrário, se você tiver otimização em:
    • Alguns vars locais serão manipulados nos registradores da CPU.
    • E se você visualizar a pilha da CPU enquanto percorre o código, notará, por exemplo, que I := I + 1 nem mesmo modifica a pilha. Então, obviamente, a mudança não pode ser realizada.
  • Você pode experimentar diferentes convenções de chamada para ver como isso afeta as coisas.
  • Você também pode testar o efeito de definir as vars locais como zero em vez de incrementá-las.
  • Isso ilustra como você depende inteiramente do que encontrou na pilha antes de seu método ser chamado.

Note que no código de exemplo que você forneceu, o registro é na verdade uma variável global, então será completamente inicializado. Se você mover todo esse código para uma function, ela será uma variável local e, portanto, de acordo com as regras dadas por Barry Kelly, apenas o campo de string será inicializado (para ”).

Eu tenho uma situação parecida, e pensei o mesmo, mas quando eu adiciono outras variables ​​usadas antes do registro, os valores se tornam lixo, então antes de usar meu registro eu tive que inicializar usando

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