我刚刚在我正在编写的程序中注意到了一些非常有趣的东西。我有一个简单的过程,用一个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位编译器实现该优化。