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
---------------------
为什么修改一个变量会影响另一个变量?
1条答案
按热度按时间ttisahbt1#
动态数组是一种引用类型。实际的数组存储在内存中的其他地方,任何引用它的数组变量都只是指向数组内存的指针。
使用
CopyMemory()
只是将一个指针从一个变量复制到另一个变量,因此您使这两个变量指向内存中的同一个物理数组(并泄漏另一个数组)。还要注意,动态数组是引用计数的,但您绕过了编译器对引用进行计数的功能。因此,最终有两个变量引用同一个数组,但其引用计数存储为1,而不是2。因此,当其中一个变量超出范围时,RTL将认为该变量是最后一次引用,并将从内存中释放数组,使另一个变量悬空。
要使用
CopyMemory()
将动态数组的 content 复制到另一个数组,而不仅仅是将 pointer 复制到源数组,您需要 dereference 数组指针以访问数组的第一个元素,例如:但是,这样做的话,在复制每个
TMen
示例的string
字段的原始字节时,就会遇到类似的问题,就像复制数组变量的原始字节一样。就像动态数组一样,string
也是一种引用类型(即,指向存储在其他地方的内存的指针),它使用引用计数进行内存管理1。因此,这种原始复制使得b
中的所有string
字段引用内存中a
字段引用的相同string
对象,但没有正确管理它们的引用计数。1:然而,在您的特定示例中,所有字符串值都是编译时常量,因此它们的引用计数为-1,因此不会为它们分配或释放动态内存。但是如果您有任何运行时创建的字符串值,因此它们的引用计数> 0,您将遇到悬挂指针的问题。
也就是说,shallow-copy动态数组(即简单地复制其引用)的 * 正确 * 方法是将一个数组变量分配给另一个数组变量,并让编译器管理引用数组的引用计数:
在这种情况下,
a
和b
引用内存中的同一个数组(其引用计数设置为2),因此修改a[0].(code|name)
也会影响b[0].(code|name)
等。否则,要 deep-copy一个动态数组(即物理地复制其所有数据),请使用编译器的
Copy()
内部函数:在这种情况下,
a
和b
引用内存中完全不同的数组(每个数组的引用计数都设置为1),因此修改a[0].(code|name)
不会影响b[0].(code|name)
等,因为它们引用内存中的不同字符串。