首页 > 解决方案 > 在 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 的相当大的程序中,这样的代码片段也能正确编译和工作。但有件事告诉我,这可能很危险......有什么建议吗?

标签: delphi

解决方案


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

您的初始代码将触发一些随机 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 约定命名变量。还要遵循相同的约定来区分代码中的静态数组和动态数组。


推荐阅读