如何使用getmem
创建2D数组?我尝试了一种类似于C的方法,但似乎没有按预期编译。
var
arr:PInteger;
begin
getmem(arr, 5*5*sizeof(integer));
arr[5]; //on this compiler produces error "Array type required"
end;
这是一项家庭作业任务,明确要求仅使用getmem
的解决方案。
答案 0 :(得分:1)
大卫在评论中指出,如果没有POINTERMATH
支持,你实际上只能做这样的事情。
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
TIntArray = array [0..0] of integer;
PIntArray = ^TIntArray;
var
p : PIntArray;
i : integer;
begin
GetMem(p, 5*5*SizeOf(integer));
for i := 0 to 24 do p^[i] := i;
for i := 0 to 24 do
WriteLn(Format('i=%d, value = %d', [i,p^[i]]));
ReadLn;
FreeMem(p);
end.
问题是关于2D数组,所以我们可以假设你想要2D索引行为。您可能认为可以扩展上述内容:
program Project1;
{$APPTYPE CONSOLE}
uses
SysUtils;
type
T2DIntArray = array [0..0, 0..0] of integer;
P2DIntArray = ^T2DIntArray;
var
p2d : P2DIntArray;
i, j : integer;
begin
GetMem(p2d, 5*5*SizeOf(integer));
for i := 0 to 4 do
for j := 0 to 4 do p2d^[i,j] := i*j;
for i := 0 to 4 do
for j := 0 to 4 do
WriteLn(Format('i=%d, j=%d, value = %d', [i,j,p2d^[i,j]]));
ReadLn;
FreeMem(p2d);
end.
但这会产生无意义的结果。从编译的程序集中,很容易看出发生了什么:
Project1.dpr.16: for i := 0 to 4 do
0041A522 33C0 xor eax,eax
0041A524 A3CC1E4200 mov [$00421ecc],eax
Project1.dpr.17: for j := 0 to 4 do p2d^[i,j] := i*j;
0041A529 33C0 xor eax,eax
0041A52B A3D01E4200 mov [$00421ed0],eax
0041A530 A1CC1E4200 mov eax,[$00421ecc] // load i into EAX
0041A535 F72DD01E4200 imul dword ptr [$00421ed0]// EAX = i*j
0041A53B 8B15C81E4200 mov edx,[$00421ec8] // load p2d into EDX
0041A541 8B0DCC1E4200 mov ecx,[$00421ecc] // load i into ECX
0041A547 8D148A lea edx,[edx+ecx*4] // offset p2d by i*SizeOf(integer)
0041A54A 8B0DD01E4200 mov ecx,[$00421ed0] // load j into ECX
0041A550 89048A mov [edx+ecx*4],eax // move i*j to EDX + j*sizeOf(integer)
0041A553 FF05D01E4200 inc dword ptr [$00421ed0]
0041A559 833DD01E420005 cmp dword ptr [$00421ed0],$05
0041A560 75CE jnz $0041a530
0041A562 FF05CC1E4200 inc dword ptr [$00421ecc]
很明显这不起作用 - 编译器希望第一个维度具有单位大小,以便最终编制索引
0,1,2,3,4,
1,2,3,4,5,
2,3,4,5,6,
......等等。
如果您将定义更改为
type
T2DIntArray = array [0..4, 0..4] of integer;
P2DIntArray = ^T2DIntArray;
然后一切都按预期工作。
Project1.dpr.16: for i := 0 to 4 do
0041A522 33C0 xor eax,eax
0041A524 A3CC1E4200 mov [$00421ecc],eax
Project1.dpr.17: for j := 0 to 4 do p2d^[i,j] := i*j;
0041A529 33C0 xor eax,eax
0041A52B A3D01E4200 mov [$00421ed0],eax
0041A530 A1CC1E4200 mov eax,[$00421ecc]
0041A535 8D0480 lea eax,[eax+eax*4] // this is more like it
0041A538 8B15C81E4200 mov edx,[$00421ec8]
0041A53E 8D0482 lea eax,[edx+eax*4] // and this..
0041A541 8B15D01E4200 mov edx,[$00421ed0]
0041A547 8B0DCC1E4200 mov ecx,[$00421ecc]
0041A54D 0FAF0DD01E4200 imul ecx,[$00421ed0]
0041A554 890C90 mov [eax+edx*4],ecx // so we get what we want
0041A557 FF05D01E4200 inc dword ptr [$00421ed0]
0041A55D 833DD01E420005 cmp dword ptr [$00421ed0],$05
0041A564 75CA jnz $0041a530
0041A566 FF05CC1E4200 inc dword ptr [$00421ecc]
但是,当然,这并不是非常灵活。您必须在编译时为类型定义固定边界,这不是很令人满意。编译器需要具有明确定义的维度大小的类型定义,以便能够在运行时正确计算索引。因此,在任何一般情况下,必须手动(使用线性索引转换)实现多维行为。
这些都不是在Delphi中做事的明智方法。我知道这是一个学校作业,但感觉有点像C程序员教授Delphi课程。如果你想以这种方式做这种事情,Delphi并不是真的合适,我不这么认为。在较新版本的Delphi中,您具有更大的灵活性(使用POINTERMATH
),但即使这样,这种编程风格的用例也必须被认为非常有限。