1

我写了两个带有 void 类型参数的方法:

procedure Method1(const MyVar; size: cardinal);
var
  Arr: array of byte;
begin
  SetLength(Arr, size);
  {now copy the data from MyVar to Arr, but how?}  
end;

procedure Method2(var MyVar; size: cardinal);
var
  Arr: array of byte;
begin
  SetLength(Arr, size);
  {return the data from the array, but how?}
end;

在第一个中,我想以字节数组的形式访问 MyVar。在第二个中,我想将数据从本地数组 Arr 复制到 MyVar。因此我使用了 CopyMemory() 函数,但它有问题。

如果我在第二种方法中使用以下内容,则只要使用数组作为参数 (Method2(Pointer(MyString)^, Length(MyString)) 或 Method2(Pointer(MyArray), Length(MyArray) 调用 Method2 就可以了)))。

CopyMemory(Pointer(MyVar), Pointer(Arr), size);

如果我使用例如整数参数 (Method2(MyInteger, SizeOf(MyInteger))) 调用 Method2,则它无法正常工作。在这种情况下,必须以这种方式调用 CopyMemory():

CopyMemory(@MyVar, Pointer(Arr), size);

如何在不知道它是简单类型(或记录)还是数组的情况下正确地从 Method2 返回数据?Method1 中的情况类似,但在这里我必须使用

CopyMemory(Pointer(Arr), Pointer(MyVar), size);

如果是数组和

CopyMemory(Pointer(Arr), @MyVar, size);

在简单类型的情况下。

当我不知道 MyVar 参数是什么时,我该怎么办?

4

2 回答 2

6

Delphi 中没有 void 类型这样的东西。您所指的称为无类型参数

无类型参数始终是实际事物本身,而不是指向您应该使用的事物的指针。CopyMemory因此,使用此类参数的正确方法是将@运算符应用于它,如下所示:

CopyMemory(@MyVar, @Arr[0], size);

请注意,我还更改了传递第二个参数的方式。如果您不依赖动态数组实际上是指向第一个元素的指针这一事实,那就更好了。如果您需要指向第一个元素的指针,请明确说明,就像我在这里所做的那样。

您的困惑来自您进行的测试,其中参数被用作指针,并且测试似乎有效。不过,我怀疑该测试的有效性。当您说Pointer(MyString)^时,您所拥有的是字符串的第一个字符。然后当您Pointer(MyVar)在函数内部说时,您将该字符类型转换为指针,这是无效的类型转换。如果您的程序似乎可以工作,那只是偶然;你的代码是错误的。

我能给出的最好的建议是除非你真的需要,否则不要进行类型转换。当你这样做时,请确保你正在类型转换的东西确实具有那种类型。在您的情况下,您不需要在将任何内容作为无类型参数传递之前进行类型转换。你可以Method1这样调用:

Method1(MyString[1], Length(MyString) * Sizeof(Char));

(我乘以,SizeOf(Char)因为我不知道你是否有 Delphi 2009。)

另外,请避免使用无类型参数。编译器可以帮助确保程序正确性的一件大事是强制执行类型安全,但是当您删除类型时,编译器将无法再为您提供帮助。

于 2009-07-08T21:40:05.623 回答
4

您的问题是您将数据或指向数据的指针传递给 Method1/2。您应该始终传递数据本身。可能你只是忘记了动态数组本身就是一个指针吗?您不应在方法中传递 A 或 Pointer(A) (此处 A 是动态数组)。通过 A[0] 或指针(A)^。

procedure Method1(const MyVar; size: cardinal);
var
  Arr: array of byte;
begin
  SetLength(Arr, size);
  CopyMemory(Pointer(Arr), @MyVar, Size);
end;

procedure Method2(var MyVar; size: cardinal);
var
  Arr: array of byte;
begin
  SetLength(Arr, size);
  Arr[0] := 1;
  Arr[1] := 2;
  Arr[2] := 3;
  Arr[3] := 4;
  CopyMemory(@MyVar, Pointer(Arr), Size);
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  I: Integer;
  A: array of Integer;
begin
  I := $01020304;
  Method1(I, 4); // we pass data itself, not the pointer to it.
  Method2(I, 4);
  SetLength(A, 2);
  A[0] := 0;
  A[1] := $01020304;
  Method1(A[0], Length(A) * SizeOf(A[0])); // note, that we pass data, not pointer
  Method2(A[0], Length(A) * SizeOf(A[0])); // A or Pointer(A) is a pointer to array's data
end;

如果 A 是动态数组:

  1. A[0] 与 Pointer(A)^ 相同,表示数组的数据。
  2. @A[0] 与 Pointer(A) 或只是 A 相同,表示数组本身,它是指向其数据的指针(以及有关负偏移的一些技术信息)。

如果 A 是静态数组:

  1. A[0] 和 A 一样,代表数组本身,也就是数组的数据。
  2. @A[0] 与@A 相同,表示指向数组的指针。
  3. Pointer(A) 或 Pointer(A)^ 没有意义。

请注意,Method1/2 中的 Arr 也是一个动态数组,这就是我们将其转换为指针的原因(CopyMemory 询问指针,而不是数据)。如果我们想使用 Move 例程(它询问数据),我们应该写 Pointer(A)^ 代替。

于 2009-07-08T21:26:32.373 回答