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