当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《Delphi-从无类型指针填充的动态数组中访问数据》经验,为你挑选了1个好方法。

我正在使用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;

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

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

谢谢.



1> Rob Kennedy..:

要在Move程序中使用动态数组,需要传递数组的第一个元素.例如:

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有着同样奇怪的无类型参数语法.


不要分配自己的内存,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)支持将任意指针类型视为数组指针.在以前的版本中,只有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;

推荐阅读
ERIK又
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有