gpt4 book ai didi

Delphi - 从由无类型指针填充的动态数组中访问数据

转载 作者:行者123 更新时间:2023-12-03 15:42:49 25 4
gpt4 key购买 nike

我正在使用 Delphi 2009 并不是因为它对我正在做的事情有很大的影响。如果我还在 2007 上,我想我会遇到同样的情况。

我有一个将数据输出到指针的 scsi 调用(错误的查看方式,但我无法解释)。

最初我使用 Move 用返回的数据填充字节 的 静态数组,但我想切换到 动态数组 在调用时已知其长度。我已经尝试了几种结果不同的事情,有些获得了数据,但有疯狂的访问违规行为,有些没有错误,但获得了无效的数据。

setlength 添加到数组中,然后使用 move ,首先会导致设置长度为空的数组,然后第二次无法通过像 OutputData[0] 访问数据,就像我在静态时所做的那样,在移动后的调试器中,一切都显示为不可访问的值或其他什么。

下面是我在阅读了一篇文章后尝试的内容,该文章说 oposit 取了一个动态数组并给出了该地址的指针。它提到了像孤立数据这样的错误。

var
Output: Pointer;
OutputData: Array of byte;
I: Integer;
begin
GetMem(Output, OutputLength.Value);
if SendPSPQuery(Char(DriveLetter[1]), cbxQuery.Items.IndexOf(cbxQuery.Text), Output, OutputLength.Value) = 0 then
begin
OutputData := @Output;
for I := 0 to OutputLength.Value - 1 do
begin
edtString.Text := edtString.Text + Char(OutputData[I]);
end;

有各种各样的其他东西,eoutput 数据用于它以字符串和十六进制形式输出。

无论如何,我如何使用指针将该数据放入一个动态数组中,然后以寻址数组的方式获取该数据。

谢谢。

最佳答案

要将动态数组与 the Move procedure 一起使用,您需要传递数组的第一个元素。例如:

var
Source: Pointer;
SourceSize: Integer;
Destination: array of Byte;

SetLength(Destination, SourceSize);
Move(Source^, Destination[0], SourceSize);

另请注意,第二个参数取消引用指针。那是因为 Move 获取您正在复制的值,而不是指向该值的指针。您正在复制指针指向的内容,因此这就是您需要传递给 Move 的内容。

顺便说一句,如果 Destination 也是一个静态数组,同样的语法也有效。你是对的,这并不是 Delphi 2009 特有的。一直到 Delphi 4 都是如此,那时引入了动态数组。并且 Move 一直有同样奇怪的 untyped parameter 语法。

不要使用 GetMem 分配自己的内存,然后进行类型转换以使编译器认为您拥有的是动态数组。 这不是 。动态数组具有普通字节缓冲区所没有的引用计数和长度字段,并且由于您无法控制编译器为访问假定的动态数组而生成的所有代码,因此您的程序可能会尝试访问数据结构中不存在的数据。

您可以让 PSP 函数将其数据直接存储到动态数组中。这是一些代码来做到这一点:
var
Output: array of Byte;

SetLength(Output, OutputLength.Value);
if SendPSPQuery(Char(DriveLetter[1]),
cbxQuery.Items.IndexOf(cbxQuery.Text),
@Output[0],
OutputLength.Value) = 0
then

之后无需释放内存;当 Output 超出范围并且没有对该数组的其他引用时,编译器会插入代码以释放动态数组。此代码采用一个动态数组并将其作为普通缓冲区传递。这是有效且安全的,因为动态数组实际上是普通旧缓冲区的子类型。该函数将接受一个指向数组第一个元素的指针,并将该指针视为指向一堆字节的指针,因为它正是如此。该函数不需要知道与程序用于动态数组簿记的那些字节相邻的附加内容。

如果您将数据放在缓冲区中,并且希望将该缓冲区视为数组,而不是将数据复制到单独的数据结构中,那么您有两种选择。
  • 声明一个静态数组指针,然后将缓冲区指针类型转换为该类型。这是经典的技术,你可以看到它在所有地方的代码中使用,尤其是早于 Delphi 4 的代码。例如:
    type
    PByteArray = ^TByteArray;
    TByteArray = array[0..0] of Byte;
    var
    ByteArray: PByteArray;

    ByteArray := PByteArray(Output);
    for i := 0 to Pred(OutputLength.Value) do begin
    {$R-}
    edtString.Text := edtString.Text + Chr(ByteArray[i]);
    {$R+}
    end;
    $R 指令是为了确保关闭该代码的范围检查,因为数组类型被声明为长度为 1。数组声明为该大小的部分原因是作为一个线索,您实际上不应该这样做声明一个该类型的变量。只能通过指针使用它。另一方面,如果您知道合适的数据最大大小是多少,您可以使用该大小来声明数组类型,然后您可以继续打开范围检查。 (如果您通常禁用范围检查,那么您只是在自找麻烦。)
  • 将您的缓冲区声明为 PByte 而不是 Pointer ,然后使用 Delphi 的新(从 Delphi 2009 开始) support for treating arbitrary pointer types as array pointers 。在之前的版本中,只有 PCharPAnsiCharPWideChar 支持这种语法。例如:
    var
    Output: PByte;

    for i := 0 to Pred(OutputLength.Value) do begin
    edtString.Text := edtString.Text + Chr(Output[i]);
    end;
    $POINTERMATH 编译器指令不需要为 PByte 启用此功能,因为该类型是在该指令生效时声明的。如果您想对其他指针类型进行类似 C 的指针操作,请将 {$POINTERMATH ON} 放在使用新扩展语法的代码之前。


  • 最后一点,您不需要一次构建一个字符的字符串。它在两个方面是浪费的。首先,您要构造大量字符串,每个字符串仅比前一个大两个字节。其次,由于您将字符串结果存储在编辑控件中,因此您也强制该控件的操作系统实现分配了一堆新字符串。将您的数据放入一个字符串中,然后一次性将其全部附加到您的编辑控件中:
    var
    OutputString: AnsiString;

    SetString(OutputString, PAnsiChar(Buffer), OutputLength.Value);
    edtString.Text := edtString.Text + OutputString;

    关于Delphi - 从由无类型指针填充的动态数组中访问数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/628965/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com