微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

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

我有一个scsi调用,它将数据输出一个指针(错误的方式查看它,但我很难解释).

最初我使用Move来填充返回的数据的Byte静态数组,但是我想切换到一个动态数组,在调用时已经知道了它的长度.我尝试了几个不同结果的东西,有些得到了数据,但有疯狂的访问权限,其他人没有错误但得到无效的数据.

将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;

使用输出数据还有其他各种因素,它会以字符串和十六进制和事物的形式输出.

无论如何,我如何使用指针将数据放入动态数组中,然后像处理数组一样获取数据.

谢谢.

解决方法

要使用带有 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分配自己的内存,然后使用type-cast使编译器认为你拥有的是动态数组.不是.动态数组具有普通字节缓冲区不具有的引用计数和长度字段,并且由于您无法控制编译器为访问假定的动态数组而生成的所有代码,因此您的程序可能会尝试访问数据结构的不存在的数据.

您可以使PSP函数将其数据直接存储到动态数组中.这是一些代码

var
  Output: array of Byte;

SetLength(Output,@Output[0],OutputLength.Value) = 0
then

之后无需释放记忆;当输出超出范围并且没有其他对数组的引用时,编译器会插入代码以释放动态数组.此代码采用动态数组并将其传递为普通缓冲区.这是有效的,因为动态数组实际上是普通旧缓冲区的子类型.该函数将接受指向数组第一个元素的指针,并将指针视为指向一堆字节的指针,因为它正是它的本质.该函数不需要知道在程序用于动态数组簿记的那些字节旁边发生了附加的东西.

如果您将数据放在缓冲区中并且希望将该缓冲区视为数组,而不是将数据复制到单独的数据结构中,那么您有两个选项.

>声明一个静态数组指针,然后将缓冲区指针类型转换为该类型.这是经典的技术,你可以看到它在代码中使用,特别是在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.在以前的版本中,只有PChar,PAnsiChar和PWideChar支持此语法.例如:

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;

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐