2009-05-14 10 views
29

J'ai donc toujours entendu dire que les champs de classe (basés sur le tas) étaient initialisés, mais pas les variables basées sur la pile. J'ai également entendu dire que les membres de l'enregistrement (également en fonction de la pile) n'étaient pas non plus initialisés. Le compilateur avertit que les variables locales ne sont pas initialisées ([Avertissement DCC] W1036 La variable 'x' n'a peut-être pas été initialisée), mais ne prévient pas les membres de l'enregistrement. J'ai donc décidé de faire un test.Quelles sont les variables initialisées en Delphi?

Je reçois toujours de Entiers et faux de booléens pour tous les éléments d'enregistrement.

J'ai essayé d'activer et de désactiver diverses options du compilateur (débogage, optimisations, etc.), mais il n'y avait aucune différence. Tous mes membres d'enregistrement sont en cours d'initialisation.

Qu'est-ce qui me manque? Je suis sur Delphi 2009 Mise à jour 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. 

Sortie:

 
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

voir aussi: http://stackoverflow.com/questions/132725/are-delphi-variables-initialized-with-a-value-by-default – Ampere

Répondre

42

Les variables globales sont zéro initialisés. Les variables utilisées dans le contexte du bloc principal begin .. end peuvent être un cas particulier; parfois ils sont traités comme des variables locales, en particulier for -loop indexers. Cependant, dans votre exemple, r est une variable globale et allouée à partir de la section .bss de l'exécutable, dont le chargeur Windows assure le remplissage par zéro.

Les variables locales sont initialisées comme si elles étaient passées à la routine Initialize. La routine Initialize utilise l'info-type d'exécution (RTTI) pour mettre à zéro les champs (récursivement - si un champ est de type tableau ou enregistrement) et les tableaux (récursivement - si le type d'élément est un tableau ou un enregistrement) d'un type managé où un type géré est l'un de:

  • AnsiString
  • UnicodeString
  • WideString
  • un type d'interface (y compris les références de procédé)
  • type tableau dynamique
  • Variant

Les allocations provenant du tas ne sont pas nécessairement initialisées; Cela dépend du mécanisme utilisé pour allouer de la mémoire. Les allocations dans le cadre des données d'objet d'instance sont remplies par TObject.InitInstance. Les allocations de AllocMem sont remplies de zéros, tandis que les allocations GetMem ne sont pas remplies de zéros. Les allocations de New sont initialisées comme si elles étaient passées à Initialize.

+1

L'important est de se souvenir que "initialisé" <> "rempli de zéro". Par exemple, l'enregistrement initialisé avec des champs de type chaîne et nombre entier ne peut pas être rempli par zéro. Bien sûr, le champ chaîne sera nul, mais le champ entier peut être <> 0. – Alex

+1

Oui - L'initialisation initialise les champs et les éléments de tableau des types gérés uniquement. –

+1

méthodes anonymes doivent être ajoutés à la liste, je pense –

1

J'ai une situation similaire, et pensait la même chose, mais quand j'ajouter d'autres variables utilisées avant l'enregistrement, les valeurs devenir des ordures, avant que j'utiliser mon disque je devais initialiser en utilisant

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

J'ai entendu dire que FillChar peut causer des problèmes si votre dossier contient des membres gérés précédemment attribués (chaînes, etc.) ou des références d'objet allouées, mais pour les nouveaux enregistrements, vous avez raison. –

+2

@Jim: Allen a répondu à une question il y a quelques jours, il a dit que FillChar n'affectera pas quand il est utilisé seulement pour l'initialisation, mais après avoir accédé à un membre de refcount et ensuite appelé fillchar, vous aurez une fuite de mémoire. –

+1

Vous pouvez appeler 'Finalize' sur l'enregistrement avant le' FillChar'. Cela garantira que tous les champs comptés par référence seront nettoyés en premier. –

1

Notez que dans l'exemple de code fourni, l'enregistrement est en fait une variable globale, il sera donc complètement initialisé.Si vous déplacez tout ce code vers une fonction, ce sera une variable locale, et ainsi, selon les règles données par Barry Kelly, seul son champ de chaîne sera initialisé (à '').

+0

Vous avez raison, j'ai essayé cela après avoir lu la réponse de Barry. –

6

Je reçois toujours 0 à partir des entiers et false à partir des booléens pour tous les membres de l'enregistrement.

J'ai essayé d'activer et de désactiver diverses options du compilateur (débogage, optimisations, etc.), mais il n'y avait aucune différence. Tous mes membres d'enregistrement sont en cours d'initialisation.

Qu'est-ce qui me manque?

Eh bien, en dehors de votre test à l'aide globale au lieu de variables locales: la chose importante que vous manque est la distinction entre les variables qui apparaissent par hasard à initialisées, et les variables qui sont actally initialisé.
BTW: C'est la raison pour laquelle les programmeurs qui ne vérifient pas leurs avertissements font l'erreur commune de supposer que leur code mal écrit se comporte correctement lorsque les quelques tests qu'ils font; arrive d'avoir 0 et par défaut Faux .... Want To Buy: random initialisation of local variables for debug builds.

Tenez compte de la variation suivante sur votre code de test:

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. 

Avec la sortie exemple suivant:

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 

Remarques

  • L'exemple fonctionne mieux si vous compilez wit h optimisation désactivée. Sinon, si vous avez une optimisation sur:
    • Certaines variables locales seront manipulées dans les registres de la CPU.
    • Et si vous visualisez la pile CPU tout en parcourant le code, vous remarquerez par exemple que I := I + 1 ne modifie même pas la pile. Donc, évidemment, le changement ne peut être réalisé.
  • Vous pouvez tester différentes conventions d'appel pour voir comment cela affecte les choses.
  • Vous pouvez également tester l'effet de la définition des variables locales à zéro au lieu de les incrémenter.
  • Ceci illustre comment vous dépendez entièrement de ce qui a trouvé son chemin sur la pile avant votre méthode a été appelée.
+3

"Les programmeurs qui ne vérifient pas leurs avertissements font l'erreur commune" - J'ai lu quelque part un conseil pour les programmeurs Delphi et depuis lors j'ai fait de cette astuce ma devise: "TRAITER LES INDICATIONS DU COMPILATEUR COMME AVERTISSEMENT ET LES AVERTISSEMENTS COMME ERREURS"! – Ampere

+2

@Altar Je ferais un pas de plus et suivrai une politique de zéro indices et avertissements. Dès que vous commencez à faire des excuses pour permettre quelques indices, vous créez une possibilité d'en oublier de nouvelles parmi des centaines d'anciennes. Tout indice/avertissement peut très facilement être réparé/évité, mais il faut beaucoup de travail ennuyeux pour en réparer des centaines. –

+0

Oui. C'est mon point: "politique de conseils et d'avertissements zéro" :) – Ampere