Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/9.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/variables/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Delphi中初始化哪些变量?_Delphi_Variables_Initialization_Delphi 2009 - Fatal编程技术网

在Delphi中初始化哪些变量?

在Delphi中初始化哪些变量?,delphi,variables,initialization,delphi-2009,Delphi,Variables,Initialization,Delphi 2009,所以我总是听说类字段(基于堆)是初始化的,但基于堆栈的变量不是。我还听说记录成员(也是基于堆栈的)也没有初始化。编译器警告未初始化局部变量([DCC Warning]W1036变量“x”可能未初始化),但不会警告记录成员。所以我决定做个测试 对于所有记录成员,我总是从整数中得到0,从布尔值中得到false 我试着打开和关闭各种编译器选项(调试、优化等),但没有区别。正在初始化我的所有记录成员 我错过了什么?我在德尔福2009更新2 program TestInitialization; {$A

所以我总是听说类字段(基于堆)是初始化的,但基于堆栈的变量不是。我还听说记录成员(也是基于堆栈的)也没有初始化。编译器警告未初始化局部变量([DCC Warning]W1036变量“x”可能未初始化),但不会警告记录成员。所以我决定做个测试

对于所有记录成员,我总是从整数中得到0,从布尔值中得到false

我试着打开和关闭各种编译器选项(调试、优化等),但没有区别。正在初始化我的所有记录成员

我错过了什么?我在德尔福2009更新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 . . . . 测试记录。 I10 I20 I30 I40 I50 s 布尔人:错 排列 0 0 0 0 0 0 0 0 0 0 0 完成。
我有一个类似的情况,我的想法也是一样的,但是当我在记录之前添加其他变量时,这些值就会变成垃圾,所以在使用记录之前,我必须使用

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

全局变量初始化为零。在程序的主
开始
块上下文中使用的变量。
结束
块可以是特例;有时它们被视为局部变量,特别是对于-循环索引器。但是,在您的示例中,
r
是一个全局变量,从可执行文件的.bss部分分配,Windows loader确保该部分为零填充

局部变量被初始化,就像它们被传递到
Initialize
例程一样。
Initialize
例程使用运行时类型信息(RTTI)将字段(递归-如果字段是数组或记录类型)和托管类型的数组(递归-如果元素类型是数组或记录)归零,其中托管类型为:

  • 安西斯廷
  • 破坏
  • 宽弦
  • 接口类型(包括方法引用)
  • 动态数组类型
  • 变体

堆中的分配不一定要初始化;这取决于用于分配内存的机制。作为实例对象数据一部分的分配由
TObject.InitInstance
填充为零。来自
AllocMem
的分配是零填充的,而
GetMem
分配不是零填充的。来自
New
的分配被初始化,就像它们被传递到
Initialize

注意,在您提供的示例代码中,记录实际上是一个全局变量,因此它将被完全初始化。如果将所有代码移动到一个函数,它将是一个局部变量,因此,根据Barry Kelly给出的规则,只有它的字符串字段将被初始化(到“”)

对于所有记录成员,我总是从整数中得到0,从布尔值中得到false

我试着打开和关闭各种编译器选项(调试、优化等),但没有区别。正在初始化我的所有记录成员

我错过了什么

好的,除了使用全局变量而不是局部变量进行测试之外:您缺少的重要一点是,巧合地出现要初始化的变量与实际初始化的变量之间的区别。

顺便说一句:这就是不检查警告的程序员经常犯错误的原因,他们认为编写糟糕的代码在进行少量测试时表现正常;碰巧有0和错误的默认值<代码>想要购买:随机初始化调试版本的局部变量。

考虑测试代码的以下变化:

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
注释

  • 如果您在编译时关闭了优化,则该示例效果最佳。否则,如果您在以下方面进行了优化:
    • 一些本地变量将在CPU寄存器中进行操作
    • 如果您在单步执行代码时查看CPU堆栈,您会注意到,例如,
      I:=I+1
      甚至不会修改堆栈。因此,很明显,这种变化是无法实现的
  • 您可以尝试不同的调用约定,看看这会如何影响事情
  • 您还可以测试将局部变量设置为零而不是递增它们的效果
  • 这说明了您如何完全依赖于在调用方法之前进入堆栈的内容

我听说,如果您的记录包含以前分配的托管成员(字符串等)或分配的对象引用,则FillChar可能会引起问题,但对于新记录,您是正确的。@Jim:Allen几天前回答了一个关于这一点的问题,他告诉我们,当FillChar仅用于初始化时,它不会产生影响,但是在访问refcount成员并调用fillchar后,您将出现内存泄漏。您可以在
fillchar
之前对记录调用
Finalize
。这将确保首先清除所有引用计数字段。重要的是记住“已初始化”“已填充零”。例如,带有字符串和整数字段的初始化记录不能为零填充。当然,字符串字段将为零,但整数字段可以为0。是-初始化仅初始化托管类型的字段和数组元素。应将匿名方法添加到列表中我认为“不检查警告的程序员会犯常见错误”-我在某个地方读过一篇针对Delphi程序员的技巧文章,从那以后,我把这篇技巧作为我的座右铭:“将编译器提示视为警告,将警告视为错误”@我会更进一步,遵循零提示和警告政策。一旦你开始找借口来允许一些暗示,你就有可能在数百个旧的暗示中遗漏新的暗示。任何提示/警告都很容易修复/避免,但是