德尔福“for ... to"语句从结束值运行到开始值 [英] Delphi "for ... to" statement runs from end value to start value

查看:22
本文介绍了德尔福“for ... to"语句从结束值运行到开始值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 Embarcadero Delphi 2010 编写一个简单的应用程序.一个带有两个循环的简单代码:

procedure TForm1.Button1Click(Sender: TObject);无功a:整数数组[0..255];我:整数;k,q:整数;开始k:=0;对于 I := 0 到 255 做开始a[i]:=i;结尾;对于 I := 0 到 255 做开始q:= a[i];k:=k+q;结尾;Label1.Caption:=inttostr(k);结尾;

根据观察列表,在第二个循环变量i"从值 256 开始并趋向于 0 (256, 255, 254, ..., 0),但数组的元素是正确的 (0, 1, 2, 3,……).变量i"仅在本地声明,没有全局变量.为什么会发生这种情况?这是正常行为吗?

解决方案

简短的回答是因为编译器优化.长答案是:

在您的 Pascal 代码中,整数 I 有两个(实际上三个)用途.首先,它是循环控制变量(或循环计数器),即它控制循环运行的次数.其次,它充当数组a 的索引.在第一个循环中,它还充当分配给数组元素的值.当编译为机器码时,这些角色由不同的寄存器处理.

如果在编译器设置中设置了优化,编译器会创建代码,将控制变量从起始值递减到零,如果可以这样做,而不改变最终结果.确实如此,因为可以避免与非零值进行比较,从而更快.

在下面的第一个循环的反汇编中,可以看到变量I的作用是这样处理的:

  • 注册eax 作为循环控制变量和值分配给数组元素
  • 注册 edx 是指向数组元素的指针(以 4(字节)每转)

拆解:

Unit25.pas.34: for I := 0 to 255 do005DB695 33C0 xor eax,eax//初始化005DB697 8D9500FCFFFF le edx,[ebp-$00000400]Unit25.pas.36: a[i]:=i;005DB69D 8902 mov [edx],eax//赋值Unit25.pas.37:结束;005DB69F 40 inc eax//准备下一轮005DB6A0 83C204 添加 edx,$04//相同Unit25.pas.34: 对于 I := 0 到 255 做005DB6A3 3D00010000 cmp eax,$00000100//与循环结束比较005DB6A8 75F3 jnz $005db69d//如果不是,则运行下一回合

由于 eax 有两个作用,所以必须向上计数.请注意,每个循环需要三个命令来管理循环计数:inc eaxcmp eax、$00000100jnz $005db69d.

第二个循环的反汇编中,变量I的作用与第一个循环中的处理类似,只是I不是分配给元素.因此循环控制只作为循环计数器,可以向下运行.

  • 注册eax是循环控制变量
  • 注册 edx 是指向数组元素的指针(以 4(字节)每转)

拆解:

Unit25.pas.39: for I := 0 to 255 do005DB6AA B800010000 mov eax,$00000100//初始化循环计数器005DB6AF 8D9500FCFFFF le edx,[ebp-$00000400]Unit25.pas.41: q:= a[i];005DB6B5 8B0A mov ecx,[edx]Unit25.pas.42: k:=k+q;005DB6B7 03D9 添加 ebx,ecxUnit25.pas.43:结束;005DB6B9 83C204 add edx,$04//准备下一轮Unit25.pas.39: 对于 I := 0 到 255 做005DB6BC 48 dec eax//递减循环计数器,包括与 0 的内在比较005DB6BD 75F6 jnz $005db6b5//jnz = 如果不为零则跳转

请注意,在这种情况下,只需要两个命令来管理循环计数:dec eaxjnz $005db6b5.

在 Delphi XE7 的 Watches 窗口中,变量 I 在第一个循环中显示为递增值,但在第二个循环中显示为 E2171 由于优化,此处无法访问变量i".在早期版本中,我记得它显示了我相信您看到的递减值.

I'm writing a simple app in Embarcadero Delphi 2010. A simple code with two cycles:

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;

According to Watch List, in second cycle variable "i" starts from value 256 and going to 0 (256, 255, 254, ..., 0), but array's elements is correct (0, 1, 2, 3, ...). Variable "i" declared only locally, no global variables. Why does this happens? Is it normal behaviour?

解决方案

The short answer is because of compiler optimization. The long answer is:

In your Pascal code, the integer I has two (actually three) purposes. First, it is the loops control variable (or loop counter), that is, it controls how many times the loop is run. Secondly, it acts as index to the array a. And in the first loop it also acts as the value assigned to the array elements. When compiled to machine code, these roles are handled by different registers.

If optimization is set in compiler settings, the compiler creates code that decrements the control variable from a start value down towards zero, if it can do so, without changing the end result. This it does, because a comparison against a non-zero value can be avoided, thus being faster.

In following disassembly of the first loop, you can see that the roles of variable I are handled as:

  • Register eax acts as loop control variable and value to be assigned to array elements
  • Register edx is pointer to array element (incremented with 4 (bytes) per turn)

disassembly:

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

Since eax has two roles, it must count upward. Note that it requires three commands for each loop to manage the loop counting: inc eax, cmp eax, $00000100 and jnz $005db69d.

In the disassembly of the second loop, the roles of variable I are handled similarily as in the first loop, except I is not assigned to the elements. Therefore the loop control only acts as a loop counter and can be run downward.

  • Register eax is loop control variable
  • Register edx is pointer to array element (incremented with 4 (bytes) per turn)

disassembly:

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

Note that in this case only two commands are needed to manage loop counting: dec eax and jnz $005db6b5.

In Delphi XE7, in the Watches window, variable I is shown during the first loop as incrementing values but during the second loop as E2171 Variable 'i' inaccessible here due to optimization. In earlier versions I recall it was showing decrementing values which I believe you see.

这篇关于德尔福“for ... to"语句从结束值运行到开始值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆