Delphi动态数组变量重用
我最近发现了一个关于 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
,因为您会假设这样的函数应该返回一个唯一的数组。但是设置 result := nil
,然后执行 setLength
看起来是错误的,不理解问题的人可能会删除 result := nil
中的 result := nil
未来认为这是多余的。
所以我的问题是,我对此的理解是否正确?是否有更好的方法来创建独特的动态数组?
I've recently discovered an interesting "gotcha" regarding dynamic arrays in Delphi and was wondering of the best way to avoid the issue.
Let's say we have the following example where a dynamic array variable is reused:
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;
As a function's result
variable is just an implict var
parameter the list
variable is being reused and as the subsequent size of the array is being increased, 5 to 8 and then to 12, then the array is being reallocated, thus the output is:
2138845992
2138930232
2138887416
But if I start with creating with a size of 12 first:
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)));
then the same dynamic array is reused, as no reallocation is needed, the output is then:
2138887416
2138887416
2138887416
This is a nasty "gotcha" as if I store each assignment of list
somewhere else then I am not getting unique arrays.
This is easy to avoid, I can either do:
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;
or
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)));
or
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)));
any of these will give me a unique array. The best seems to be result := nil
, as you would assume such a function should return a unique array. But setting result := nil
, then doing a setLength
just looks wrong and someone not understanding the issue may remove the result := nil
in the future thinking it is redundant.
So my question is, is my understanding of this correct and is there a better way to create a unique dynamic array?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
“如果我将列表的每个分配存储在其他地方,那么我就不会获得唯一的数组。”
如果您获取每个列表的副本,则该列表不会被重用,因为动态数组是引用计数的。
使用以下代码再次运行测试:
日志每次都会显示不同的值。
"if I store each assignment of list somewhere else then I am not getting unique arrays."
If you take a copy of each list, then the list is not reused, because dynamic arrays are reference-counted.
Run your test again with this code:
The log will show different values each time.