delphi – 局部变量和TPair阵列 – 内存分配的奇怪行为

我有以下代码示例编译在delphi xe5 update 2中.
procedure TForm1.FormCreate(Sender: TObject);
var i,t:Integer;
    buf: array [0..20] of TPair<Integer,Integer>;
begin
  t := 0;
  for i := Low(buf) to High(buf) do begin
    ShowMessage(
      Format(
        'Pointer to i = %p;'#$d#$a+
        'Pointer to buf[%d].Key = %p;'#$d#$a+
        'Pointer to buf[%d].Value = %p;'#$d#$a+
        'Pointer to t = %p',[@i,i,@(buf[i].Key),@(buf[i].Value),@t]
      )
    );
    buf[i].Key := 0;
    buf[i].Value := 0;
    t := t + 1;
  end;
end;

如果我运行它,它会显示变量的地址.
变量i和t在buf的内存范围内具有地址!
当ireaches 3时,赋值buf [i] .Value:= 0;覆盖i的前3个字节和t的最后一个字节.这导致无限循环,因为当它达到3时,我得到重置为0.
如果我用SetLength(buf,20)自己分配内存;一切安好.

图片显示了我的意思.

我的设置:

> Windows 7 64位
> Delphi XE 5 Update 2
>调试配置32位

奇怪,不是吗?
有人可以重现吗?
是delphi编译器中的错误吗?

谢谢.

编辑:
这是同样的例子,但也许更好地了解我的意思:

和btw:对不起,我的英文不好)

解决方法

这肯定是一个编译器的bug.它只影响在堆栈上分配的TPair阵列.例如,这个编译和运行正常:
program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Generics.Collections;

var i:Integer;
    buf: array [0..20] of TPair<Integer,Integer>;
begin
  for i := Low(buf) to High(buf) do begin
    buf[i].Key := 0;
    buf[i].Value := 0;
  end;
end.

但是,这显示错误

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Generics.Collections;    

procedure DoSomething;
var i:Integer;
    buf: array [0..20] of TPair<Integer,Integer>;
begin
  for i := Low(buf) to High(buf) do begin
    buf[i].Key := 0;
    buf[i].Value := 0;
  end;
end;

begin
  DoSomething;
end.

编译器似乎错误地计算了TPair< Integer,Integer>的大小.编译的汇编显示如下前言:

Project1.dpr.14: begin
00445C50 55               push ebp
00445C51 8BEC             mov ebp,esp
00445C53 83C4E4           add esp,-$1c  //***  Allocate only 28 bytes (7words)
Project1.dpr.15: for i := Low(buf) to High(buf) do begin
00445C56 33C0             xor eax,eax
00445C58 8945FC           mov [ebp-$04],eax
Project1.dpr.16: buf[i].Key := 0;
00445C5B 8B45FC           mov eax,[ebp-$04]
00445C5E 33D2             xor edx,edx
00445C60 8954C5E7         mov [ebp+eax*8-$19],edx
Project1.dpr.17: buf[i].Value := 0;
00445C64 8B45FC           mov eax,[ebp-$04]
00445C67 33D2             xor edx,edx
00445C69 8954C5EB         mov [ebp+eax*8-$15],edx
Project1.dpr.18: end;
00445C6D FF45FC           inc dword ptr [ebp-$04]
Project1.dpr.15: for i := Low(buf) to High(buf) do begin
00445C70 837DFC15         cmp dword ptr [ebp-$04],$15
00445C74 75E5             jnz $00445c5b
Project1.dpr.19: end;
00445C76 8BE5             mov esp,ebp
00445C78 5D               pop ebp
00445C79 C3               ret 
00445C7A 8BC0             mov eax,eax

编译器只在堆栈上分配了7个双字.第一个是整数i,只留下分配给TPair阵列的6个双词,这是不够的(SizeOf(TPair <整数,整数)等于8 - >两个双字).在第三次迭代中,mov [ebp eax * 8- $15],edx(即:buf [2] .Value)运行到i的堆栈位置,并将其值设置为零. 您可以通过在堆栈上强制足够的空间来演示一个工作程序:

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  Generics.Collections;


procedure DoSomething;
var i:Integer;
    fixalloc : array[0..36] of Integer; // dummy variable
                                        // allocating enough space for
                                        // TPair array
    buf: array [0..20] of TPair<Integer,Integer>;
begin
  for i := Low(buf) to High(buf) do begin
    buf[i].Key := i;
    buf[i].Value := i;
  end;
end;

begin
  DoSomething;
end.

这是在XE2中进行测试,但是如果您还看到问题,似乎至少还会持续XE5.

相关文章

 从网上看到《Delphi API HOOK完全说明》这篇文章,基本上都...
  从网上看到《Delphi API HOOK完全说明》这篇文章,基本上...
ffmpeg 是一套强大的开源的多媒体库 一般都是用 c/c+&#x...
32位CPU所含有的寄存器有:4个数据寄存器(EAX、EBX、ECX和ED...
1 mov dst, src dst是目的操作数,src是源操作数,指令实现的...