使用getmem

时间:2016-04-06 09:11:17

标签: arrays delphi delphi-7

如何使用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的解决方案。

1 个答案:

答案 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),但即使这样,这种编程风格的用例也必须被认为非常有限。