我刚刚在我正在编写的程序中注意到了一些非常有趣的东西。我有一个简单的过程,用一个x类型的对象填充一个TStringlist。

我在跟踪问题时添加了一个断点,偶然发现了这个问题,希望有人可以解释它为什么发生,或者链接到相关文档,因为我找不到任何东西。

我的循环从0到11。我正在使用的指针是通过nPtr:= 0在循环中初始化的,但是当程序运行时,nPtr var从12下降到1。循环如代码片段所示,但是发生了同样的事情。该变量在单位中的其他任何地方都不能使用。

我问了一个与我合作过的人,谁说这是由于Delphi优化引起的,但是我想知道为什么以及如何决定应该影响哪个循环。

谢谢你的帮助。

代码:

procedure TUnit.ProcedureName;
var
    nPtr : Integer;
    obj : TObject;
begin
nPtr:=0;//added later
for nPtr := 0 to 11 do
    begin
    obj := TObject.Create(Self);
    slPeriodList.AddObject('X', obj);
    end;
end;

最佳答案

仅当循环主体未引用循环变量时,才可能进行优化。在这种情况下,如果循环的下限为零,则编译器将反转循环。

如果循环变量从未被循环体引用,则编译器可以合理地实现循环。所需要做的就是按照循环界限的要求多次执行循环体。确实,编译器在优化循环变量方面将是完全合理的。

考虑以下程序:

{$APPTYPE CONSOLE}

procedure Test1;
var
  i: Integer;
begin
  for i := 0 to 11 do
    Writeln(0);
end;

procedure Test2;
var
  i: Integer;
begin
  for i := 0 to 11 do
    Writeln(i);
end;

begin
  Test1;
  Test2;
end.
Test1的主体由XE7(32位Windows编译器)编译为该代码,带有发行选项:

Project1.dpr.9:对于i:= 0至11
00405249 BB0C000000 mov ebx,$ 0000000c
Project1.dpr.10:Writeln(0);
0040524E A114784000 mov eax,[$ 00407814]
00405253 33D2 XOR EDX,EDX
00405255 E8FAE4FFFF调用@ Write0Long
0040525A E8D5E7FFFF调用@WriteLn
0040525F E800DBFFFF调用@_IOTest
Project1.dpr.9:对于i:= 0至11
00405264 4B dec ebx
00405265 75E7 jnz $ 0040524e

编译器正在向下运行循环,这可以通过使用dec看到。注意,循环终止的测试是使用jnz进行的,不需要cmp。那是因为dec对零执行隐式比较。
dec的文档中指出以下内容:



当且仅当ZF指令的结果为零时,才会设置dec标志。而ZF是确定jnz是否分支的因素。
Test2发出的代码是:

Project1.dpr.17:对于i:= 0至11
0040526D 33DB异或ebx,ebx
Project1.dpr.18:Writeln(i);
0040526F A114784000 mov eax,[$ 00407814]
00405274 8BD3 mov edx,ebx
00405276 E8D9E4FFFF调用@ Write0Long
0040527B E8B4E7FFFF调用@WriteLn
00405280 E8DFDAFFFF调用@_IOTest
00405285 43 inc ebx
Project1.dpr.17:对于i:= 0至11
00405286 83FB0C cmp ebx,$ 0c
00405289 75E4 jnz $ 0040526f

请注意,循环变量正在增加,并且我们现在有一个额外的cmp指令,该指令在每次循环迭代时执行。

有趣的是,64位Windows编译器不包含此优化。对于Test1,它产生以下内容:

Project1.dpr.9:对于i:= 0至11
00000000004083A5 4833DB异或rbx,rbx
Project1.dpr.10:Writeln(0);
00000000004083A8 488B0D01220000 mov rcx,[rel $ 00002201]
00000000004083AF 4833D2 XOR RDX,RDX
00000000004083B2 E839C3FFFF调用@ Write0Long
00000000004083B7 4889C1 mov rcx,rax
00000000004083BA E851C7FFFF调用@WriteLn
00000000004083BF E86CB4FFFF调用@_IOTest
00000000004083C4 83C301添加ebx,$ 01
Project1.dpr.9:对于i:= 0至11
00000000004083C7 83FB0C cmp ebx,$ 0c
00000000004083CA 75DC jnz Test1 + $ 8美元

我不确定为什么没有在64位编译器中实现此优化。我的猜测是,该优化在实际情况下的影响可忽略不计,设计人员选择不花费精力为64位编译器实现该优化。

10-08 19:57