我正在使用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;
使用输出数据还有其他各种因素,它会以字符串和十六进制的形式输出.
无论如何,我如何使用指针将数据放入动态数组中,然后像处理数组一样获取数据.
谢谢.
要在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;