在Delphi中通过静态类型对动态数组进行类型转换

时间:2018-12-10 07:44:46

标签: delphi

问:在静态数组上强制转换动态数组是否安全?

type
  Bytearray = array of byte;

function calcCRC(buf1: pointer) : dword;    
var
  buf: ByteArray ;
  outbuffer : array [1..30] of byte;
begin
  buf := bytearray(buf1^); // <- is it safe ?
  outbuffer[1] := buf[0];
end;

procedure test;
var
  testarr : array [1..30] of byte ;
begin
  calccrc(@testarr);
end ;

即使在使用FastMM4的大型程序中,此类代码段也可以编译并正常工作。但是有件事告诉我,这可能很危险... 有什么建议吗?

2 个答案:

答案 0 :(得分:2)

我想您需要恢复逻辑。将静态数组转换为动态数组根本不安全(它在实际数据之前有一个计数器和引用计数),而相反(将动态数组转换为静态数组指针)是绝对安全的,如果您要注意的话缓冲区长度。

您的初始代码将触发一些随机的GPF(访问冲突),原因是在calcCRC()中的某些隐藏代码中将修改参考计数器-使用F2查看ASM:超出您的期望,特别是call DynArrayClear,这是非常不安全的。由于内存中的内容,您还没有任何问题。但是如果在数据之前存储一个1整数,它将触发GPF,因为保留calcCRC()将使Delphi RTL尝试释放动态数组实例。

如果希望使用索引访问内存字节,则需要使用指向静态数组而不是动态数组的指针。

代码可能如下:

Type
  TByteDynArray = array of byte ;
  TByteArray = array[0 .. (maxInt div sizeof(byte)) - 1] of byte;
  PByteArray = ^TByteArray;

function calcCRCptr(buf1: PByteArray; buf1len: integer): dword;   
var
  outbuffer : array [1..30] of byte;
begin
  result := 0;
  // ensure you don't create any access violation by using big indexes
  if buf1len < 1 then
    exit; // avoid GPF
  outbuffer[1] := buf1[0];
  ...
end;

function calcCRCdynarray(const buf1: TByteDynArray): dword;   
begin
  // you can use length(buf1) to get the number of items/bytes
  result := calcCRCptr(pointer(buf1), length(buf1));
end;

procedure test ;
var
  testdynarr: TByteDynArray;
  teststaticarray: array[0..10] of byte;
begin
  Setlength(testdynarr, 100);
  calccrcdynarray(testdynarr) ;     // safe
  calccrcptr(pointer(testdynarr), length(testdynarr)); // direct call
  calccrcptr(@teststaticarray, 11); // OK
end;

还要确保您不会弄乱指针,例如您可以使用有关值(T ...)和指针(P ....)的适当Delphi约定来命名变量。还应遵循相同的约定,以区分代码中的静态数组和动态数组。

答案 1 :(得分:1)

这种方法当然是不安全的。静态阵列与动态阵列的内存布局不同。动态数组的元数据包含引用计数和长度。在这段简短的摘录中,您可能会不喜欢它,但是不建议您使用它。

无论是什么问题,这都不是解决方案。可能的解决方案可能涉及使用开放数组或指向字节的指针。