Jakie zmienne są inicjalizowane w Delphi?

Więc zawsze słyszałem, że pola klasy (oparte na stercie) były inicjalizowane, ale zmienne oparte na stosie nie były. Słyszałem też, że członkowie rekordu (również oparty na stosie) również nie zostali zainicjalizowani. Kompilator ostrzega, że zmienne lokalne nie są inicjalizowane ([DCC Warning] W1036 zmienna 'x' mogła nie być inicjalizowana), ale nie ostrzega o członach rekordów. Więc postanowiłem przeprowadzić test.

I always get 0 from Integers and false from Booleans dla wszystkich członków zespołu.

Próbowałem włączyć różne opcje kompilatora (debugowanie, optymalizacje, itp.) włączane i wyłączane, ale nie było żadnej różnicy. Wszystkie moje nagrania są inicjowane.

Co mi umyka? Jestem na 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 ',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.

Wyjście:

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 . . . .

Author: Jim McKeeth, 2009-05-14

4 answers

Zmienne globalne są inicjalizowane zerem. Zmienne używane w kontekście głównego begin..end blok programu może być szczególnym przypadkiem; czasami są one traktowane jako zmienne lokalne, szczególnie for-indeksery pętli. Jednak w twoim przykładzie {[3] } jest zmienną globalną i przypisaną z .sekcja bss pliku wykonywalnego, którą Windows loader Zapewnia jest wypełniona zerem.

Zmienne lokalne są inicjalizowane tak, jakby zostały przekazane do procedury Initialize. Initialize rutynowe zastosowania Typ runtime-info (RTTI) do pól zerowych (rekurencyjnie - jeśli pole jest typu tablicy lub rekordu) i tablic (rekurencyjnie-jeśli Typ elementu jest tablicą lub rekordem) typu zarządzanego, gdzie Typ zarządzany jest jednym z:

  • AnsiString
  • UnicodeString
  • WideString
  • Typ interfejsu (w tym odniesienia do metod)
  • typ tablicy dynamicznej
  • Variant

Alokacje ze sterty niekoniecznie są inicjowane; to zależy jaki mechanizm został użyty do alokacji pamięci. Alokacje jako część danych obiektu instancji są wypełnione zerem przez TObject.InitInstance. Przydziały z {[7] } są wypełnione zerem, podczas gdy GetMem przydziały nie są wypełnione zerem. Przydziały z {[9] } są inicjalizowane tak, jakby zostały przekazane do Initialize.

 44
Author: Barry Kelly,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-06-21 00:10:34

Zawsze dostaję 0 z liczb całkowitych i false z wartości logicznych dla wszystkich członków rekordu.

Próbowałem włączyć różne opcje kompilatora (debugowanie, optymalizacje, itp.) włączane i wyłączane, ale nie było żadnej różnicy. Wszystkie moje nagrania są inicjowane.

Co mi umyka?

Cóż, oprócz testu używającego zmiennych globalnych zamiast lokalnych: ważną rzeczą, której brakuje, jest rozróżnienie między zmiennymi, które przypadkowo pojawiają się do zainicjowania, A zmienne, które actally are zainicjowany.
BTW: to jest powód, dla którego programiści, którzy nie sprawdzają ostrzeżeń, popełniają powszechny błąd zakładając, że ich źle napisany kod zachowuje się poprawnie podczas kilku testów, które wykonują; zdarza się, że mają 0 I False defaults.... Want To Buy: random initialisation of local variables for debug builds.

Rozważ następującą zmianę kodu testu:

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.

Z następującą próbką wyjście:

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

Uwagi

  • przykład działa najlepiej, jeśli kompilujesz z wyłączoną optymalizacją. W przeciwnym razie, jeśli masz optymalizację na:
    • niektóre lokalne var będą manipulowane w rejestrach procesora.
    • i jeśli przeglądasz stos procesora podczas przechodzenia przez kod, zauważysz na przykład, że I := I + 1 nawet nie modyfikuje stosu. Więc oczywiście zmiana nie może zostać przeprowadzona.
  • możesz eksperymentować z różnymi nazwami konwencje, aby zobaczyć, jak to wpływa na rzeczy.
  • Możesz również przetestować efekt ustawienia lokalnych VAR-ów na zero zamiast ich zwiększania.
  • to ilustruje, jak jesteś całkowicie zależny od tego, co znalazło się na stosie przed twoja metoda została wywołana.
 7
Author: Disillusioned,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-04-24 19:56:27

Zauważ, że w przykładowym kodzie, który podałeś, rekord jest w rzeczywistości zmienną globalną, więc zostanie całkowicie zainicjowany. Jeśli przeniesiesz cały ten kod do funkcji, będzie to zmienna lokalna, i tak, zgodnie z regułami podanymi przez Barry 'ego Kelly' ego, tylko jej pole string zostanie zainicjowane (to ").

 1
Author: Frederik Slijkerman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-05-14 09:56:26

Mam podobną sytuację i myślałem tak samo, ale kiedy dodaję inne zmienne używane przed rekordem, wartości stają się śmieciami, więc zanim użyję mojego rekordu musiałem zainicjować za pomocą

FillChar(MyRecord, SizeOf(MyRecord), #0)
 1
Author: Cesar Romero,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-12-20 09:14:04