Ответ 1
Это определенно похоже на ошибку компилятора. Он влияет только на массив 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
, оставляя только 6 dwords, выделенных для массива TPair
, которого недостаточно (SizeOf(TPair<integer,integer>)
равно 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, если вы также видите проблему.