SetLength/Move - 导致内存损坏

Wod*_*dzu 5 delphi memory-management delphi-2009 multidimensional-array

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

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.
Run Code Online (Sandbox Code Playgroud)

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

在原始问题中,我在循环中从BoundryInfo中删除元素,这就是我调用的原因 Finalize(BoundryInfo[BoundryLength])

Arn*_*hez 10

在你的代码中,

Move(BoundryInfo[PointIndex+1], BoundryInfo[PointIndex], 
  ((BoundryLength - 1) - PointIndex) * SizeOf(BoundryInfo[PointIndex]));
Run Code Online (Sandbox Code Playgroud)

将指针复制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
Run Code Online (Sandbox Code Playgroud)

简而言之:

  • 完成将在其中重写的项目move();
  • 将nil写入最新项目,该项目由复制项目复制move().

  • @Wodzu - 仅当您了解数据的内存布局以及引用计数的工作原理时才使用此移动黑客.别忘了. (4认同)
  • 我展示的代码类似于[我关于如何从动态数组中删除项目的文章](http://www.cs.wisc.edu/~rkennedy/array-delete). (3认同)

Dav*_*nan 5

通过使用Move和颠覆动态数组引用计数机制,您只需为自己设置陷阱.我强烈建议你坚持标准机制,让编译器担心细节.每次都会让他们正确.

for i := 0 to high(BoundaryInfo)-1 do
  BoundaryInfo[i] := BoundaryInfo[i+1];
SetLength(BoundaryInfo, Length(BoundaryInfo)-1);
Run Code Online (Sandbox Code Playgroud)