Delphi 动态数组变量重用

Delphi dynamic array variable reuse

我最近在 Delphi 中发现了一个关于动态数组的有趣“陷阱”,想知道避免该问题的最佳方法。

假设我们有以下示例,其中重复使用了动态数组变量:

function FillArray(Count: Integer): TArray<Integer>;
var
  i: Integer;
begin
  SetLength(result, Count);
  for i := 0 to Count - 1 do
    result[i] := i;
end;

procedure TfrmMain.Button1Click(Sender: TObject);
var
  list: TArray<Integer>;
begin
  list := FillArray(5);
  meLog.Lines.Add(IntToStr(NativeInt(list)));

  list := FillArray(8);
  meLog.Lines.Add(IntToStr(NativeInt(list)));

  list := FillArray(12);
  meLog.Lines.Add(IntToStr(NativeInt(list)));
end;

作为一个函数的 result 变量只是一个隐含的 var 参数,list 变量被重用并且随着数组的后续大小增加,从 5 到 8 和然后到 12,然后数组被重新分配,因此输出是:

2138845992
2138930232
2138887416

但是如果我先从 12 大小开始创建:

  list := FillArray(12);
  meLog.Lines.Add(IntToStr(NativeInt(list)));

  list := FillArray(8);
  meLog.Lines.Add(IntToStr(NativeInt(list)));

  list := FillArray(5);
  meLog.Lines.Add(IntToStr(NativeInt(list)));

然后重复使用相同的动态数组,因为不需要重新分配,输出是:

2138887416
2138887416
2138887416

这是一个令人讨厌的“陷阱”,就好像我将 list 的每个赋值存储在其他地方然后我没有得到唯一的数组。

这很容易避免,我可以这样做:

function FillArray(Count: Integer): TArray<Integer>;
var
  i: Integer;
begin
  result := nil;
  SetLength(result, Count);
  for i := 0 to Count - 1 do
    result[i] := i;
end;

  list := nil;
  list := FillArray(12);
  meLog.Lines.Add(IntToStr(NativeInt(list)));

  list := nil;
  list := FillArray(8);
  meLog.Lines.Add(IntToStr(NativeInt(list)));

  list := nil;
  list := FillArray(5);
  meLog.Lines.Add(IntToStr(NativeInt(list)));

  list := Copy(FillArray(12));
  meLog.Lines.Add(IntToStr(NativeInt(list)));

  list := Copy(FillArray(8));
  meLog.Lines.Add(IntToStr(NativeInt(list)));

  list := Copy(FillArray(5));
  meLog.Lines.Add(IntToStr(NativeInt(list)));

其中任何一个都会给我一个独特的数组。最好的似乎是 result := nil,因为您会假设这样的函数应该 return 是一个唯一的数组。但是设置 result := nil,然后做 setLength 看起来是错误的,不理解这个问题的人可能会在将来删除 result := nil,认为它是多余的。

所以我的问题是,我的理解是否正确,是否有更好的方法来创建唯一的动态数组?

“如果我将列表的每个分配存储在其他地方,那么我就不会得到唯一的数组。”

如果您复制每个列表,则该列表不会重用,因为动态数组是reference-counted.

运行 使用此代码再次测试:

procedure TfrmMain.Button1Click(Sender: TObject);
var
  list,
  list1,
  list2: TArray<Integer>;
begin
  list := FillArray(12);
  meLog.Lines.Add(IntToStr(NativeInt(list)));
  list1 := list;

  list := FillArray(8);
  meLog.Lines.Add(IntToStr(NativeInt(list)));
  list2 := list;

  list := FillArray(5);
  meLog.Lines.Add(IntToStr(NativeInt(list)));
end;

日志每次都会显示不同的值。