【问题标题】:How do you pass an array by reference in Delphi?如何在 Delphi 中通过引用传递数组?
【发布时间】:2009-04-03 15:28:22
【问题描述】:

我已经阅读了关于通过引用传递的内容,所以

procedure test(var x:integer);
begin
  x:=x+5;
end;

所以上面的代码通过引用更新了 5。我假设如果我通过引用更新数组,我可以声明 var X: array of blah... 有一些绑定错误,只是想知道我是否应该使用指针的数据类型到数据或指针是否始终为 int ......所以我知道这是我通过引用传递的方式还是我的代码中的其他内容是问题所在。

【问题讨论】:

    标签: delphi parameters reference types


    【解决方案1】:

    如果您将动态数组作为非 var 参数传递,编译器将进行复制。

    下面的小代码示例通过在表单标题中显示 37/42 来证明这一点。

    procedure IncArray1(data: array of integer);
    var i : integer;
    begin
      for i := Low(data) to High(data) do
        data[i] := data[i] + 5;
    end;
    
    procedure IncArray2(var data: array of integer);
    var i : integer;
    begin
      for i := Low(data) to High(data) do
        data[i] := data[i] + 5;
    end;
    
    procedure TForm8.FormCreate(Sender: TObject);
    var
      data: array of integer;
    begin
      SetLength(data, 1);
      data[0] := 37;
      IncArray1(data);
      Caption := IntToStr(data[0]);
      IncArray2(data);
      Caption := Caption + '/' + IntToStr(data[0]);
    end;
    

    如果我们查看生成的汇编代码,IncArray1 以

    开头
    004552B4 8BCA             mov ecx,edx
    004552B6 85C9             test ecx,ecx
    004552B8 7807             js $004552c1
    004552BA 8B1C88           mov ebx,[eax+ecx*4]
    004552BD 49               dec ecx
    004552BE 53               push ebx
    004552BF 79F9             jns $004552ba
    004552C1 8BC4             mov eax,esp
    

    此代码将源数组复制到堆栈并将 eax 设置为第一个元素的地址(= 最后一次推送后存储在堆栈指针中的地址)。堆栈向下增长,因此代码从最后一个元素开始(调用 IncArray1 时 edx 包含 High(data))并重复(读取元素;推送元素;递减索引)直到到达元素 0。

    IncArray2 不包含此类代码。调用者在调用 IncArray2 之前将数据的地址存储到 eax 寄存器中,而 IncArray2 只是使用该地址。

    如果您出于任何原因不想使用“var”,您可以将数据的地址传递给您的方法。但是由于你不能在参数声明中使用语法'data: ^array of integer',你必须为你的数据声明一个类型。而且你必须在方法中的任何地方都使用 'data^' 而不是 'data'。

    type
      TData = array of integer;
      PData = ^TData;
    
    procedure IncArray(data: PData);
    var i : integer;
    begin
      for i := Low(data^) to High(data^) do
        data^[i] := data^[i] + 5;
    end;
    
    procedure TForm8.FormCreate(Sender: TObject);
    var
      data: TData;
    begin
      SetLength(data, 2);
      data[0] := 37;
      IncArray(@data);
      Caption := IntToStr(data[0]);
    end;
    

    使用 Delphi 2007 测试。

    【讨论】:

    • 好的,有没有办法做到这一点,我有一个大数组,我想通过参考来处理。复制数组非常昂贵。
    • 是的,就像上面代码中的 IncArray2 一样 - 用户 'var' 前缀。
    • @gabr,你能检查当你不变异时是否复制了动态数组?我知道字符串是写时复制的。
    • 数据仍被复制。如果您想阻止 Delphi 复制数据,请在您的参数前加上 'const'。也适用于记录、字符串和接口。
    【解决方案2】:

    Gabr 的回答是正确的,但关键点埋得很深,我将把它作为单独的帖子提出:

    首先定义你的类型!在这种特定情况下,编译器在那里接受了一个整数数组,但这只是因为它具有特殊含义并且它 NOT 是您所期望的。在过程定义中定义类型的任何其他尝试都将失败。

    与 C 不同,如果你想让两个东西赋值兼容,你必须将它们声明为 SAME 类型,而不仅仅是构建相同的两个类型:

    Var
         A : Array [1..4] of Integer;
         B : Array [1..4] of Integer;
    
    Begin
        A := B;
    

    不会编译。而是:

    Type
        Array4 = array [1..4] of Integer;
    
    Var
        A : Array4;
        B : Array4;
    
    Begin
        A := B;
    

    编译器会按照您的预期进行。

    【讨论】:

    • 在 Gabr 的帖子之后跑去更改代码,我现在有了你所说的确切问题,它不会堆积起来,它说正式类型应该是相同的.. A 和 B 为我在不同的单位..我该怎么办?
    • 假设单元 A“使用”单元 B。在单元 B 中声明共享类型,两者都可以“看到”该定义。
    • 如果您运行 gabr 的代码,但所有“整数数组”都替换为 TIntArray(其中 TIntArray = 整数数组),则数组始终通过引用传递,并且 data[0] 为 37 + 5 + 5 = 49。这不是很奇怪吗?
    • @dan-gph。当你定义一个数组的类型时,你就有了一个动态数组。动态数组总是通过引用传递。另一方面,具有像“X 数组”这样的参数的方法需要一个“开放数组”,这个开放数组的规则是不同的,请参阅here
    猜你喜欢
    • 1970-01-01
    • 2014-06-11
    • 1970-01-01
    • 2011-08-09
    • 2013-11-07
    • 2010-11-09
    • 1970-01-01
    相关资源
    最近更新 更多