Итак, я всегда слышал, что поля классов (на основе кучи) были инициализированы, но переменные на основе стека не были. Я также слышал, что элементы записи (также основанные на стеках) также не были инициализированы. Компилятор предупреждает, что локальные переменные не инициализированы ([Предупреждение DCC] W1036 Переменная "x", возможно, не была инициализирована), но не предупреждает о членах записи. Поэтому я решил провести тест.
Я всегда получаю 0 от целых чисел и false от Booleans для всех участников записи.
Я попытался включить и выключить различные параметры компилятора (отладка, оптимизация и т.д.), но не было никакой разницы. Все мои участники записи инициализируются.
Что мне не хватает? Я на 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( ',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.
Вывод:
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 . . . .
Глобальные переменные инициализируются нулем. Переменные, используемые в контексте основного блока begin
.. end
программы, могут быть особым случаем; иногда они рассматриваются как локальные переменные, особенно for
-loop indexers. Однако в вашем примере r
является глобальной переменной и выделяется из секции .bss исполняемого файла, которую обеспечивает загрузчик Windows.
Локальные переменные инициализируются так, как если бы они были переданы в процедуру Initialize
. В подпрограмме Initialize
используется тип-info (RTTI) времени выполнения для нулевых полей (рекурсивно - если поле имеет массив или тип записи) и массивы (рекурсивно - если тип элемента - это массив или запись) управляемый тип, где управляемый тип является одним из:
Выделения из кучи не обязательно инициализируются; это зависит от того, какой механизм был использован для выделения памяти. Выделения в качестве части данных объекта экземпляра заполняются нулями TObject.InitInstance
. Выделения из AllocMem
заполняются нулями, а GetMem
распределения не заполняются нулями. Выделения из New
инициализируются так, как если бы они были переданы в Initialize
.
Я всегда получаю 0 из целых чисел и false из Booleans для всех элементов записи.
Я попытался включить и выключить различные параметры компилятора (отладка, оптимизация и т.д.), но не было никакой разницы. Все мои участники записи инициализируются.
Что мне не хватает?
Хорошо, помимо вашего теста, используя глобальные вместо локальных переменных: важно, чтобы вы отсутствовали, - это различие между переменными, которые случайно появляются для инициализации, а переменные, которые действуют.
BTW. Именно поэтому программисты, которые не проверяют свои предупреждения, делают распространенную ошибку, предполагая, что их плохо написанный код ведет себя правильно, когда выполняется несколько тестов; имеют 0 и False значения по умолчанию.... Want To Buy: random initialisation of local variables for debug builds.
Рассмотрим следующий вариант тестового кода:
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.
Со следующим выходом:
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
Примечания
I := I + 1
даже не модифицирует стек. Поэтому очевидно, что изменение не может быть выполнено.Обратите внимание, что в приведенном вами примере кода запись на самом деле является глобальной переменной, поэтому она будет полностью инициализирована. Если вы переместите весь этот код в функцию, это будет локальная переменная, и поэтому в соответствии с правилами, данными Барри Келли, будет инициализировано только его строковое поле (до '').
У меня такая же ситуация, и я думал то же самое, но когда я добавляю другие переменные, используемые до записи, значения становятся мусором, поэтому, прежде чем использовать свою запись, мне пришлось инициализировать с помощью
FillChar(MyRecord, SizeOf(MyRecord), #0)