代码之家  ›  专栏  ›  技术社区  ›  Jim McKeeth

在Delphi中初始化哪些变量?

  •  31
  • Jim McKeeth  · 技术社区  · 15 年前

    所以我总是听说类字段(基于堆)是初始化的,但基于堆栈的变量不是。我还听说记录成员(也是基于堆栈的)也没有初始化。编译器警告本地变量未初始化([dcc warning]w1036变量“x”可能尚未初始化),但不对记录成员发出警告。所以我决定做个测试。

    我总是得到 整数 布尔值 所有记录成员。

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

    我错过了什么?我在Delphi2009更新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 . . . .
    
    
    4 回复  |  直到 12 年前
        1
  •  44
  •   Barry Kelly    13 年前

    全局变量初始化为零。在主上下文中使用的变量 begin end 程序块可能是一种特殊情况;有时它们被视为局部变量,特别是 for -循环索引器。但是,在您的示例中, r 是一个全局变量,从可执行文件的.bss部分分配,Windows加载器确保该部分为零填充。

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

    • 弦线
    • 单列字符串
    • 宽字符串
    • 接口类型(包括方法引用)
    • 动态数组类型
    • 变体

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

        2
  •  7
  •   Disillusioned    13 年前

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

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

    我错过了什么?

    好吧,除了使用全局变量而不是局部变量进行测试之外:您缺少的重要内容是变量之间的区别 巧合地出现 要初始化的变量,以及 通常是 初始化。
    顺便说一句 :这就是那些不检查警告的程序员犯了一个常见错误的原因,即当他们做了很少的测试时,假设他们写得不好的代码的行为是正确的;恰好有0和错误的默认值…. 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
    

    笔记

    • 如果您在关闭优化的情况下编译,那么该示例的效果最好。否则,如果您优化了:
      • 一些本地变量将在CPU寄存器中操作。
      • 如果您在单步执行代码时查看CPU堆栈,您会注意到例如 I := I + 1 甚至不修改堆栈。所以很明显,这种变化是无法实现的。
    • 您可以使用不同的调用约定进行试验,以了解这是如何影响事物的。
    • 您还可以测试将本地变量设置为零而不是增加它们的效果。
    • 这说明了如何完全依赖于堆栈中找到的方法 之前 调用了您的方法。
        3
  •  1
  •   Frederik Slijkerman    15 年前

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

        4
  •  1
  •   bluish dmajkic    12 年前

    我有一个类似的情况,我也这么想,但是当我添加在记录之前使用的其他变量时,这些值变成垃圾,所以在使用我的记录之前,我必须使用

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