delphi - SetLength/Move - 导致内存损坏

标签 delphi memory-management multidimensional-array delphi-2009

今天我偶然发现了一个导致我的数组损坏的问题。这是一个可重现的测试用例:

unit Unit40;

interface

type
  TVertex = record
    X, Y: Double;
  end;

  TEdge = record
    V1, V2: TVertex;
  end;
  TEdges = array of TEdge;

type
  TBoundryInfo = array of TEdges;

procedure MemoryCorrupt;

implementation

procedure MemoryCorrupt;
var
  BoundryInfo: TBoundryInfo;
  i, PointIndex, BoundryLength: Integer;
begin
  BoundryLength := 57;
  PointIndex := 0;
  SetLength(BoundryInfo, BoundryLength);
  for i := 0 to BoundryLength - 1 do
  begin
    if i <> 17 then
    begin
      SetLength(BoundryInfo[i], 1);
      BoundryInfo[i][0].V1.X := 1;
      BoundryInfo[i][0].V2.X := 1;
      BoundryInfo[i][0].V1.Y := 1;
      BoundryInfo[i][0].V2.Y := 1;
    end else
    begin
      SetLength(BoundryInfo[i], 2);
      BoundryInfo[i][0].V1.X := 1;
      BoundryInfo[i][0].V2.X := 1;
      BoundryInfo[i][0].V1.Y := 1;
      BoundryInfo[i][0].V2.Y := 1;
      BoundryInfo[i][1].V1.X := 1;
      BoundryInfo[i][1].V2.X := 1;
      BoundryInfo[i][1].V1.Y := 1;
      BoundryInfo[i][1].V2.Y := 1;
    end;
  end;
  BoundryLength := 9;
  SetLength(BoundryInfo, BoundryLength);
  Move(BoundryInfo[PointIndex+1], BoundryInfo[PointIndex],
    ((BoundryLength - 1) - PointIndex) * SizeOf(BoundryInfo[PointIndex]));
  Dec(BoundryLength);
  Finalize(BoundryInfo[BoundryLength]);
  SetLength(BoundryInfo, BoundryLength); //After this, arrays contains garbage
  BoundryInfo[0][0].V1.X := 3;
end;

end.

我猜想最后一次SetLength之后的内存损坏只是Move使用不当的症状。 有人可以向我解释我做错了什么以及在这种情况下如何正确使用 Move 吗?

在最初的问题中,我在循环中从 BoundryInfo 中删除元素,这就是为什么我调用 Finalize(BoundryInfo[BoundryLength])

最佳答案

在您的代码中,

Move(BoundryInfo[PointIndex+1], BoundryInfo[PointIndex], 
  ((BoundryLength - 1) - PointIndex) * SizeOf(BoundryInfo[PointIndex]));

BoundryInfo[PointIndex+1] 的指针复制到 BoundryInfo[PointIndex]。该指针是另一个动态数组,您必须注意引用计数。

即:

SetLength(BoundryInfo[PointIndex],0); // release memory
Move(BoundryInfo[PointIndex+1], BoundryInfo[PointIndex], 
  ((BoundryLength - 1) - PointIndex) * SizeOf(BoundryInfo[PointIndex]));
PPointerArray(BoundryInfo)^[BoundryLength-1] := nil; // avoid GPF

简而言之:

  • 完成在 move() 期间将被覆盖的项目;
  • 将 nil 写入最新项目,该项目由 move() 复制。

关于delphi - SetLength/Move - 导致内存损坏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12111061/

相关文章:

delphi - 清除并重新加载组和项目后,TListView 组和项目不会出现

c - 以下 C 代码中的内存泄漏

php - PHP构建多维json数组

delphi - 在 Delphi TStringList 中分割文本的更快方法

windows - Message-Only Window 消耗的资源更少吗?

python - 在 Python 中使用 <128KB 的字符串时内存泄漏?

c - 通过汇编查找数组的维度

C++通过引用函数传递数组但内容保持不变

windows - 为什么对 GetClipRgn 的结果调用 GetRgnBox 会返回与 GetClipRect 截然不同的矩形?

iphone - 由于内存问题/MBProgressHUD 指示器导致应用程序崩溃