所以我总是听说类字段(基于堆)已初始化,但基于堆栈的变量不是。我还听说记录成员(也是基于堆栈的)也没有初始化。编译器警告局部变量未初始化([DCC警告] W1036变量'x'可能尚未初始化),但不会对记录成员发出警告。所以我决定进行测试。
对于所有记录成员,我总是从整数获得 0 ,从 Booleans 获得 false 。
我尝试打开和关闭各种编译器选项(调试,优化等),但没有区别。我的所有唱片成员都在初始化。
我错过了什么?我正在使用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.
输出:
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 :(得分:45)
全局变量是零初始化的。在程序的主begin
.. end
块的上下文中使用的变量可以是特殊情况;有时它们被视为局部变量,尤其是for
- 循环索引器。但是,在您的示例中,r
是一个全局变量,并从可执行文件的.bss部分进行分配,Windows加载程序确保该部分为零填充。
初始化局部变量,就像将它们传递给Initialize
例程一样。 Initialize
例程使用运行时类型信息(RTTI)来清零字段(递归地 - 如果字段是数组或记录类型)和数组(递归地 - 如果元素类型是数组或记录)托管类型,托管类型是以下之一:
堆中的分配不一定是初始化的;它取决于用于分配内存的机制。作为实例对象数据的一部分的分配由TObject.InitInstance
填零。来自AllocMem
的分配是零填充,而GetMem
分配不是零填充。来自New
的分配已初始化,就好像它们已传递到Initialize
。
答案 1 :(得分:7)
对于所有记录成员,我总是从Integers获得0,从Booleans获得false。
我尝试打开和关闭各种编译器选项(调试,优化等),但没有区别。我的所有唱片成员都在初始化。
我错过了什么?
那么,除了使用全局变量而不是局部变量进行测试之外:您缺少的重要一点是 巧合地出现 的变量之间的区别,以及 初始化 的变量
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
甚至不会修改堆栈。显然,这种改变无法实现。答案 2 :(得分:1)
我有类似的情况,并且想法一样,但是当我在记录之前添加其他变量时,值变为垃圾,所以在我使用我的记录之前我必须使用
进行初始化FillChar(MyRecord, SizeOf(MyRecord), #0)
答案 3 :(得分:1)
请注意,在您提供的示例代码中,记录实际上是一个全局变量,因此它将被完全初始化。如果将所有代码移动到一个函数,它将是一个局部变量,因此,根据Barry Kelly给出的规则,只会初始化它的字符串字段(到'')。