【问题标题】:Reading and assigning a void type parameter读取和分配 void 类型参数
【发布时间】:2009-07-08 20:56:01
【问题描述】:

我写了两个带有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 (Method2(Pointer(MyString)^, Length(MyString)) 或 Method2(Pointer(MyArray),长度(MyArray)))。

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 参数是什么时,我该怎么办?

【问题讨论】:

    标签: delphi parameters void


    【解决方案1】:

    Delphi 中没有 void 类型。你指的是untyped parameter

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

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

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

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

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

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

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

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

    【讨论】:

      【解决方案2】:

      您的问题是您将数据或指向数据的指针传递给 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)^ 来代替。

      【讨论】:

      • 感谢两位的回答。似乎我以错误的方式调用了我的方法,这就是为什么我得到 AV 异常并认为必须以两种不同的方式调用 CopyMemory() 函数的原因。感谢您的解释,我设法轻松找到了我的错误。
      • @亚历山大。你的总结太好了。
      • 这可能是我过去 3 天来第十次阅读您的帖子。非常感谢您的贡献。
      猜你喜欢
      • 2023-04-05
      • 1970-01-01
      • 1970-01-01
      • 2021-10-22
      • 1970-01-01
      • 2021-04-18
      • 2021-02-12
      • 2021-08-24
      • 1970-01-01
      相关资源
      最近更新 更多