我刚刚在一个正在编写的程序中发现了一些非常有趣的东西。我有一个简单的过程,用一个类型为x的对象填充TStringlist。
我在跟踪问题时添加了一个断点,偶然发现这个问题是希望有人能够解释它为什么会发生,或者链接到相关文档,因为我找不到任何东西。
我的循环从0到11开始。我正在使用的指针在循环中初始化为nPtr:= 0但是当程序运行时,nPtr var从12变为1.然后我初始化了在代码片段中显示的循环外部的var,但同样的事情发生了。该变量在单位中没有其他地方使用。
我问过一位与我合作的人,他们说这是由于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;
答案 0 :(得分:9)
只有在循环体不引用循环变量时才可以进行优化。在这种情况下,如果循环的下限为零,则编译器将反转循环。
如果循环体从未引用循环变量,那么编译器在实现循环时是合理的,但它很高兴。所需要做的就是按照循环边界的要求多次执行循环体。实际上,编译器在优化循环变量方面是完全合理的。
考虑这个程序:
{$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: for i := 0 to 11 do 00405249 BB0C000000 mov ebx,$0000000c Project1.dpr.10: Writeln(0); 0040524E A114784000 mov eax,[$00407814] 00405253 33D2 xor edx,edx 00405255 E8FAE4FFFF call @Write0Long 0040525A E8D5E7FFFF call @WriteLn 0040525F E800DBFFFF call @_IOTest Project1.dpr.9: for i := 0 to 11 do 00405264 4B dec ebx 00405265 75E7 jnz $0040524e
编译器正在向下运行循环,使用dec
可以看出。请注意,循环终止测试是使用jnz
执行的,不需要cmp
。这是因为dec
执行与零的隐式比较。
dec
的文档说明如下:
受影响的标志
CF标志不受影响。设置OF,SF,ZF,AF和PF标志 根据结果。
当且仅当ZF
指令的结果为零时,才设置dec
标志。而ZF
决定了jnz
分支。
为Test2
发出的代码是:
Project1.dpr.17: for i := 0 to 11 do 0040526D 33DB xor ebx,ebx Project1.dpr.18: Writeln(i); 0040526F A114784000 mov eax,[$00407814] 00405274 8BD3 mov edx,ebx 00405276 E8D9E4FFFF call @Write0Long 0040527B E8B4E7FFFF call @WriteLn 00405280 E8DFDAFFFF call @_IOTest 00405285 43 inc ebx Project1.dpr.17: for i := 0 to 11 do 00405286 83FB0C cmp ebx,$0c 00405289 75E4 jnz $0040526f
请注意,循环变量正在增加,我们现在有一条额外的cmp
指令,在每次循环迭代时执行。
值得注意的是,64位Windows编译器不包含此优化。对于Test1
,它产生了这个:
Project1.dpr.9: for i := 0 to 11 do 00000000004083A5 4833DB xor rbx,rbx Project1.dpr.10: Writeln(0); 00000000004083A8 488B0D01220000 mov rcx,[rel $00002201] 00000000004083AF 4833D2 xor rdx,rdx 00000000004083B2 E839C3FFFF call @Write0Long 00000000004083B7 4889C1 mov rcx,rax 00000000004083BA E851C7FFFF call @WriteLn 00000000004083BF E86CB4FFFF call @_IOTest 00000000004083C4 83C301 add ebx,$01 Project1.dpr.9: for i := 0 to 11 do 00000000004083C7 83FB0C cmp ebx,$0c 00000000004083CA 75DC jnz Test1 + $8
我不确定为什么在64位编译器中没有实现此优化。我的猜测是,优化在实际情况下的影响可以忽略不计,设计人员选择不花费精力为64位编译器实现它。