type
TMen=record
code:String;
name:String;
end;
TMenLst=array of TMen;
TForm10 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
a,b:TMenLst;
public
{ Public declarations }
procedure show(v:TMenLst);
end;
var
Form10: TForm10;
implementation
{$R *.dfm}
procedure TForm10.Button1Click(Sender: TObject);
begin
SetLength(a,3);
a[0].code:='code1';
a[0].name:='name1';
a[1].code:='code2';
a[1].name:='name2';
a[2].code:='code3';
a[2].name:='name3';
SetLength(b,3);
CopyMemory(@b,@a,SizeOf(a));
//Move(a, b, SizeOf(a));
a[0].code:='aaaa';
a[0].name:='bbbb';
show(a);
show(b);
end;
procedure TForm10.show(v: TMenLst);
var
I:integer;
begin
for I := Low(v) to High(v) do
Memo1.Lines.Add('code:'+a[I].code+' '+'name:'+a[I].name);
Memo1.Lines.Add('---------------------');
end;
结果:
code:aaaa name:bbbb
code:code2 name:name2
code:code3 name:name3
---------------------
code:aaaa name:bbbb
code:code2 name:name2
code:code3 name:name3
---------------------
为什么修改一个变量会影响另一个变量?
最佳答案
动态数组是一种引用类型。实际的数组存储在内存中的其他位置,引用它的任何数组变量都只是指向数组内存的指针。
您对CopyMemory()
的使用只是将一个指针从一个变量复制到另一个变量,因此您使这两个变量指向内存中的同一个物理数组(并泄漏另一个数组)。
另请注意,动态数组是引用计数的,但您绕过了编译器计算引用的能力。因此,最终有 2 个变量引用同一个数组,但其引用计数存储为 1,而不是 2。因此,当其中一个变量超出范围时,RTL 会认为该变量是最后一个引用并将释放内存中的数组,让另一个变量悬空。
使用CopyMemory()
要将动态数组的内容复制到另一个数组,而不是仅仅将指针复制到源数组,您需要取消引用该数组访问数组第一个元素的指针,例如:
procedure TForm10.Button1Click(Sender: TObject);
begin
SetLength(a,3);
a[0].code:='code1';
a[0].name:='name1';
a[1].code:='code2';
a[1].name:='name2';
a[2].code:='code3';
a[2].name:='name3';
SetLength(b,Length(a));
CopyMemory(@b[0],@a[0],SizeOf(TMen)*Length(a));
//or:
//CopyMemory(Pointer(b),Pointer(a),SizeOf(TMen)*Length(a));
//Move(a[0], b[0], SizeOf(TMen)*Length(a));
//Move(Pointer(a)^, Pointer(b)^, SizeOf(TMen)*Length(a))
a[0].code:='aaaa';
a[0].name:='bbbb';
show(a);
show(b);
end;
但是,通过这样做,当您复制 string
的原始字节时,您会遇到类似的问题。每个 TMen
的字段例如,当您复制数组变量的原始字节时。就像动态数组一样,string
也是一种引用类型(即,指向存储在其他地方的内存的指针),它使用引用计数进行内存管理1。因此,这种原始复制使得所有 string
b
中的字段引用同string
内存中的对象 a
的字段引用,但没有正确管理它们的引用计数。
1:但是,在您的特定示例中,所有字符串值都是编译时常量,因此它们的引用计数为-1,因此不会为它们分配或释放动态内存。但是,如果您有任何运行时创建的字符串值,因此它们的引用计数 > 0,您就会遇到悬空指针的问题。
话虽这么说,浅复制动态数组(即简单地复制其引用)的正确方法是将一个数组变量分配给另一个数组变量,然后让编译器管理引用数组的引用计数:
procedure TForm10.Button1Click(Sender: TObject);
begin
SetLength(a,3);
a[0].code:='code1';
a[0].name:='name1';
a[1].code:='code2';
a[1].name:='name2';
a[2].code:='code3';
a[2].name:='name3';
b := a;
a[0].code:='aaaa';
a[0].name:='bbbb';
show(a);
show(b);
end;
在本例中,a
和b
引用内存中的同一个数组(引用计数设置为2),因此修改 a[0].(code|name)
也会影响b[0].(code|name)
等
否则,要深度复制动态数组(即以物理方式为其所有数据创建新副本),请使用编译器的 Copy()
内在函数:
procedure TForm10.Button1Click(Sender: TObject);
begin
SetLength(a,3);
a[0].code:='code1';
a[0].name:='name1';
a[1].code:='code2';
a[1].name:='name2';
a[2].code:='code3';
a[2].name:='name3';
b := Copy(a);
a[0].code:='aaaa';
a[0].name:='bbbb';
show(a);
show(b);
end;
在本例中,a
和b
引用内存中完全不同的数组(每个数组的引用计数设置为1),因此修改 a[0].(code|name)
不影响b[0].(code|name)
等,因为它们引用内存中的不同字符串。
关于delphi - 将动态记录数组的变量复制到另一个变量,但它们共享一个地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76277109/