如何在Delphi中通过引用传递数组?

时间:2022-10-23 21:43:26

I already read up about passing by reference and so

我已经读过关于通过引用传递等等

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

so the above code updates 5 by reference. I assumed if I was updating an array by reference I could declare var X: array of blah... having some bound bugs and just wanted to know if I should be using the type of data for the pointer to the data or if the pointer is always int... just so I know if it's how I'm doing my passing by reference or something else in my code that is the issue.

所以上面的代码通过引用更新5。我假设我是通过引用更新数组我可以声明var X:blah数组...有一些绑定错误,只是想知道我是否应该使用数据类型指向数据的指针或指针永远是int ...只是所以我知道我是如何通过引用传递或我的代码中的其他东西是问题。

2 个解决方案

#1


If you pass dynamic array as a non-var parameter, compiler will make a copy.

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

The small code sample below demonstrates that by displaying 37/42 in the form caption.

下面的小代码示例演示了通过在表单标题中显示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;

If we look into the generated assembler code, IncArray1 starts with

如果我们查看生成的汇编程序代码,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

This code copies source array to the stack and sets eax to the address of the first element (= address stored in the stack pointer after last push). Stack grows down so the code starts with the last element (edx contains High(data) when IncArray1 is called) and repeats (read element; push element; decrement index) until it gets to the element 0.

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

IncArray2 contains no such code. Caller stores the address of the data into the eax register before calling IncArray2 and IncArray2 just uses this address.

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

In case you don't want to use 'var' for any reason, you can pass an address of the data to your method. But as you can't use syntax 'data: ^array of integer' in parameter declaration, you'd have to declare a type for your data. And you'd have to use 'data^' instead of 'data' everywhere in the method.

如果您因任何原因不想使用'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;

Tested with Delphi 2007.

用Delphi 2007测试。

#2


Gabr's answer is correct but the key point is buried deep enough that I'll bring it out as a separate post:

加布尔的答案是正确的,但关键点是埋藏得足够深,我会把它作为一个单独的帖子把它拿出来:

Define your types first! In this specific case the compiler accepted an array of integer there, but that's only because that has a special meaning and it's NOT what you expected. Any other attempt to define a type in the procedure's definition would simply have failed.

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

Unlike C, if you want two things to be assignment compatible you have to declare them as being the SAME type, not merely two types that are built the same:

与C不同,如果要将两个事物分配兼容,则必须将它们声明为SAME类型,而不仅仅是两个构建相同的类型:

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

Begin
    A := B;

Won't compile. Rather:

不会编译。而是:

Type
    Array4 = array [1..4] of Integer;

Var
    A : Array4;
    B : Array4;

Begin
    A := B;

and the compiler does what you would expect.

并且编译器会执行您期望的操作。

#1


If you pass dynamic array as a non-var parameter, compiler will make a copy.

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

The small code sample below demonstrates that by displaying 37/42 in the form caption.

下面的小代码示例演示了通过在表单标题中显示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;

If we look into the generated assembler code, IncArray1 starts with

如果我们查看生成的汇编程序代码,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

This code copies source array to the stack and sets eax to the address of the first element (= address stored in the stack pointer after last push). Stack grows down so the code starts with the last element (edx contains High(data) when IncArray1 is called) and repeats (read element; push element; decrement index) until it gets to the element 0.

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

IncArray2 contains no such code. Caller stores the address of the data into the eax register before calling IncArray2 and IncArray2 just uses this address.

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

In case you don't want to use 'var' for any reason, you can pass an address of the data to your method. But as you can't use syntax 'data: ^array of integer' in parameter declaration, you'd have to declare a type for your data. And you'd have to use 'data^' instead of 'data' everywhere in the method.

如果您因任何原因不想使用'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;

Tested with Delphi 2007.

用Delphi 2007测试。

#2


Gabr's answer is correct but the key point is buried deep enough that I'll bring it out as a separate post:

加布尔的答案是正确的,但关键点是埋藏得足够深,我会把它作为一个单独的帖子把它拿出来:

Define your types first! In this specific case the compiler accepted an array of integer there, but that's only because that has a special meaning and it's NOT what you expected. Any other attempt to define a type in the procedure's definition would simply have failed.

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

Unlike C, if you want two things to be assignment compatible you have to declare them as being the SAME type, not merely two types that are built the same:

与C不同,如果要将两个事物分配兼容,则必须将它们声明为SAME类型,而不仅仅是两个构建相同的类型:

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

Begin
    A := B;

Won't compile. Rather:

不会编译。而是:

Type
    Array4 = array [1..4] of Integer;

Var
    A : Array4;
    B : Array4;

Begin
    A := B;

and the compiler does what you would expect.

并且编译器会执行您期望的操作。