Delphi 如何将动态数组作为非类型常量传递?

Delphi 如何将动态数组作为非类型常量传递?,delphi,delphi-xe6,Delphi,Delphi Xe6,我正在调用一个函数,该函数需要: 非类型的const 字节长度 as是一种常见的Delphi模式。例如: procedure DoStuff(const Data; DataLen: Integer); var data: TBytes; begin data := GetData; DoStuff(data[0], Length(0)); 在这个示例测试用例中,allDoStuff是否确保接收到四个字节: 0x21 0x43 0x65 0x87 //Pass f

我正在调用一个函数,该函数需要:

  • 非类型的const
  • 字节长度
as是一种常见的Delphi模式。例如:

procedure DoStuff(const Data; DataLen: Integer);
var
   data: TBytes;
begin
   data := GetData;
   DoStuff(data[0], Length(0));
在这个示例测试用例中,all
DoStuff
是否确保接收到四个字节:

0x21 0x43  0x65  0x87
//Pass four bytes in QuadWord
var qw: Int64;
qw := $7FFFFFFF87654321;
DoStuff(qw, 4); //works
出于测试目的,我们将在little endian Intel机器上将该字节序列解释为32位无符号整数:
0x87654321
。这使得完整的测试功能:

procedure DoStuff(const Data; DataLen: Integer);
var
    pba: PByteArray;
    plw: PLongWord;
begin
    //Interpret data as Longword (unsigned 32-bit)
    plw := PLongWord(@Data);
    if plw^ <> $87654321 then
        raise Exception.Create('Fail');

    //Interpret data as array of bytes
    pba := PByteArray(@Data);
    if (pba[0] <> $21) or (pba[1] <> $43) or (pba[2] <> $65) or (pba[3] <> $87) then
        raise Exception.Create('Fail');

//  ShowMessage('Success');
end;
因此,将
长单词
传递给接受非类型的常量的函数的方法就是只传递变量。即:

  • 错误
    @lw
  • 错误
    地址(lw)
  • 错误
    指针(@lw)^
  • 正确
    lw
我还可以传递一个8字节的类型;因为我只读取前四个字节:

0x21 0x43  0x65  0x87
//Pass four bytes in QuadWord
var qw: Int64;
qw := $7FFFFFFF87654321;
DoStuff(qw, 4); //works
阵列 现在我们得到了一个稍微复杂一点的东西:传递数组:

//Pass four bytes in an array
var data: array[0..3] of Byte;
data[0] := $21;
data[1] := $43;
data[2] := $65;
data[3] := $87;
DoStuff(data[0], 4); //Works

//Pass four bytes in a dynamic array
var moreData: TBytes;
SetLength(moreData, 4);
moreData[0] := $21;
moreData[1] := $43;
moreData[2] := $65;
moreData[3] := $87;
DoStuff(moreData[0], 4); //works
这两者都能正常工作。但在你们中的一些人犹豫之前,让我们看看更棘手的情况:

//Pass four bytes at some point in an array
var data: array[0..5] of Byte;
data[2] := $21;
data[3] := $43;
data[4] := $65;
data[5] := $87;
DoStuff(data[2], 4); //Works

//Pass four bytes at some point in a dynamic array
var moreData: TBytes;
SetLength(moreData, 6);
moreData[2] := $21;
moreData[3] := $43;
moreData[4] := $65;
moreData[5] := $87;
DoStuff(moreData[2], 4); //works
由于
untyped const
运算符隐式地按引用传递,因此我们传递的引用从数组中的第3个字节开始(并且没有浪费的临时数组副本)

根据这一点,我可以推断出这样一条规则:如果我想将一个数组传递给一个非类型的const,则传递一个索引数组:

DoStuff(data[n], ...);
两个问题 第一个问题是如何将空的动态数组传递给非类型的const函数。例如:

procedure DoStuff(const Data; DataLen: Integer);
var
   data: TBytes;
begin
   data := GetData;
   DoStuff(data[0], Length(0));
如果
数据
为空(由于范围检查错误),此常规代码将失败

另一个问题是一些人对传递
数据[0]
而不是简单地使用
数据的语法的批评:

//Pass four bytes in an array without using the array index notation
data[0] := $21;
data[1] := $43;
data[2] := $65;
data[3] := $87;
DoStuff(data, 4); //works
这确实有效。这确实意味着我失去了以前的能力:传递索引数组。但真正的问题是,当与动态数组一起使用时,它会失败:

//Pass four bytes in a dynamic array without using the array index notation
SetLength(moreData, 4);
moreData[0] := $21;
moreData[1] := $43;
moreData[2] := $65;
moreData[3] := $87;
DoStuff(moreData, 4); //FAILS
问题在于,有一个关于如何实现动态数组的内部实现细节。动态数组实际上是指针,而数组实际上是数组

这是否意味着如果要传递数组,我必须找出它是哪种类型,并使用不同的变通语法

//real array
DoStuff(data, 4); //works for real array
DoStuff(data, 4); //fails for dynamic array
DoStuff(Pointer(data)^, 4); //works for dynamic array
这真的是我应该做的吗?难道没有更正确的方法吗

奖金变通办法 因为我不想失去索引数组的能力:

DoStuff(data[67], 4);
我可以保留索引符号:

DoStuff(data[0], 4);
只需确保处理边缘情况:

if Length(data) > 0 then
   DoStuff(data[0], 4)
else
   DoStuff(data, 0); //in this case data is dummy variable

所有这些的全部要点是不要在内存中复制数据;但是通过引用来传递它。

这真的很简单

DoStuff(data, ...);           // for a fixed length array
DoStuff(Pointer(data)^, ...); // for a dynamic array
这是正确的方法。动态数组是指向第一个元素的指针,如果数组为空,则为
nil


一旦您放弃类型安全性并使用非类型化参数,那么在调用此类函数时只需要稍微多一些摩擦就可以了。

对于动态数组:
DoStuff(指针(数据)^,Min(4,长度(数据))@DavidHeffernan删除我在发布时错过的最后一个“预期”值是一个很好的捕获。
//Pass four bytes in a LongWord
var lw: LongWord;
lw := $87654321;
DoStuff(lw, 4); //works