我正在Embarcadero Delphi 2010中编写一个简单的应用程序.一个包含两个周期的简单代码:
procedure TForm1.Button1Click(Sender: TObject); var a:array [0..255] of integer; i:integer; k,q:integer; begin k:=0; for I := 0 to 255 do begin a[i]:=i; end; for I := 0 to 255 do begin q:= a[i]; k:=k+q; end; Label1.Caption:=inttostr(k); end;
根据监视列表,在第二个循环变量“i”从值256开始并且转到0(256,255,254,…,0),但是数组的元素是正确的(0,1,2,3,… ).变量“i”仅在本地声明,没有全局变量.
为什么会这样?这是正常的行为吗?
解决方法
简短的回答是因为编译器优化.答案很长的答案是:
在你的Pascal代码中,整数我有两个(实际上是三个)目的.首先,它是循环控制变量(或循环计数器),也就是说,它控制循环运行的次数.其次,它充当数组a的索引.在第一个循环中,它还充当分配给数组元素的值.编译为机器代码时,这些角色由不同的寄存器处理.
如果在编译器设置中设置了优化,则编译器会创建代码,将控制变量从起始值向下递减为零(如果可以),而不更改最终结果.这样做,因为可以避免与非零值的比较,因此更快.
在下面的第一个循环的反汇编中,您可以看到变量I的角色被处理为:
>寄存器eax充当循环控制变量和值
分配给数组元素
>寄存器edx是指向数组元素的指针(以4递增)
每回合(字节)
拆卸:
Unit25.pas.34: for I := 0 to 255 do 005DB695 33C0 xor eax,eax // init 005DB697 8D9500FCFFFF lea edx,[ebp-$00000400] Unit25.pas.36: a[i]:=i; 005DB69D 8902 mov [edx],eax // value assignment Unit25.pas.37: end; 005DB69F 40 inc eax // prepare for next turn 005DB6A0 83C204 add edx,$04 // same Unit25.pas.34: for I := 0 to 255 do 005DB6A3 3D00010000 cmp eax,$00000100 // comparison with end of loop 005DB6A8 75F3 jnz $005db69d // if not,run next turn
由于eax有两个角色,它必须向上计数.请注意,每个循环需要三个命令来管理循环计数:inc eax,cmp eax,$00000100和jnz $005db69d.
在第二个循环的反汇编中,变量I的角色与第一个循环中的角色类似,除了我没有分配给元素.因此,循环控制仅用作循环计数器并且可以向下运行.
>寄存器eax是循环控制变量
>寄存器edx是指向数组元素的指针(以4递增)
每回合(字节)
拆卸:
Unit25.pas.39: for I := 0 to 255 do 005DB6AA B800010000 mov eax,$00000100 // init loop counter 005DB6AF 8D9500FCFFFF lea edx,[ebp-$00000400] Unit25.pas.41: q:= a[i]; 005DB6B5 8B0A mov ecx,[edx] Unit25.pas.42: k:=k+q; 005DB6B7 03D9 add ebx,ecx Unit25.pas.43: end; 005DB6B9 83C204 add edx,$04 // prepare for next turn Unit25.pas.39: for I := 0 to 255 do 005DB6BC 48 dec eax // decrement loop counter,includes intrinsic comparison with 0 005DB6BD 75F6 jnz $005db6b5 // jnz = jump if not zero
请注意,在这种情况下,只需要两个命令来管理循环计数:dec eax和jnz $005db6b5.
在Delphi XE7中,在Watches窗口中,变量I在第一个循环期间显示为递增值,但在第二个循环期间为E2171变量’i’由于优化而无法访问.在早期版本中,我记得它显示了我相信你看到的递减值.