delphi - 如何将动态数组作为无类型常量传递?

标签 delphi delphi-xe6

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

  • 无类型const
  • 字节长度

as 是常见的 Delphi 模式。例如:

procedure DoStuff(const Data; DataLen: Integer);

在此示例测试用例中,所有 DoStuff 都会确保它接收到四个字节:

0x21 0x43  0x65  0x87

出于测试目的,我们将在小端 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;

出于说明目的,已阐明了错误检查。

测试

我可以开始测试是否可以正确地将字节传递到我的 DoStuff 函数。

//Pass four bytes in a LongWord
var lw: LongWord;
lw := $87654321;
DoStuff(lw, 4); //works

因此,将 LongWord 传递给采用非类型化 const 的函数的方法就是传递变量。即:

  • 错误:@lw
  • 错误:Addr(lw)
  • :指针(@lw)^
  • 正确:lw

我还可以传递8字节类型;因为我只读取了前四个字节:

//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

由于无类型 const 运算符通过引用隐式传递,因此我们从数组中的第 3 个字节开始传递引用(并且我们没有浪费的临时数组副本)。

由此我可以推断出以下规则:如果我想将数组传递给无类型的 const,则传递索引数组:

DoStuff(data[n], ...);

两个问题

第一个问题是我如何将动态数组传递给非类型化const函数。例如:

var
   data: TBytes;
begin
   data := GetData;
   DoStuff(data[0], Length(0));

如果data为空(由于范围检查错误),此通用代码将失败。

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

//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。

一旦放弃类型安全并使用无类型参数,调用此类函数时预计会遇到更多摩擦,这是合理的。

关于delphi - 如何将动态数组作为无类型常量传递?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31926630/

相关文章:

delphi - for ... in 带接口(interface)

delphi - 是否可以将 Form.Width 设置为 10000 像素?

database - 如何执行存储在数据库中的代码?

delphi - 从滚动列表中删除动态创建的面板

delphi - 使用指向数组中的两个 UInt64 加载 xmm 寄存器

delphi - 如何对动态数组进行切片?

multithreading - 在 Delphi 线程中使用 CoInitialize

delphi - 如何影响 2 个动画对象的 2 个角度?

delphi - 已弃用的命令 StrPas

delphi - 如何拦截发送到窗体上任何 TWinControl 的消息?