背景1
var text:String;
text:='hello';
myFunc(text);
上下文2
function myFunc(mytext:String);
var textcopy:String;
begin
textcopy:=mytext;
end;
从Context1调用Context2上的 myFunc
,局部变量mytext
指向Context2之外的内存?或者mytext
在范围内有自己的内存空间,并使用text
的相同内容填充/复制?我可能遗漏了一些非常基本的东西,因为我收到了access violation
错误。
有没有办法明确指定一个函数是应该通过引用还是按值接收参数,然后像C一样复制?我不确定我是怎么做的。
答案 0 :(得分:23)
Delphi字符串的内存管理有点不寻常。在您致电myFunc(text)
并指定textcopy := mytext
后,所有三个变量(text
,mytext
和textcopy
)将指向同一地址,即原始字符串。
但是只要您使用其中一个变量对字符串进行更改,Delphi就会在后台克隆字符串,并将更改应用于副本。另外两个变量仍指向原始变量,因此它们保持不变。因此,在Context 1中不会看到上下文2中所做的任何更改 - 这种“写时复制”机制有效地为您提供了按值传递的语义。所有这些字符串都是引用计数的,并且一旦所有引用都超出范围,它们将自动释放。
然而,有一个例外。如果使用指针而不是字符串操作访问字符串,则可以绕过复制步骤,更改将影响原始字符串。您还将绕过引用计数逻辑,并可能最终得到一个指向已释放的内存块的指针。这可能是您违反访问权限的原因,但我不能说没有更多细节/更多代码。
如果您想要引用传递,请将您的函数声明为myFunc(var mytext: String)
。如果你想强制Delphi复制字符串,而不是等到它被修改,你可以使用System.UniqueString
。
答案 1 :(得分:13)
在Delphi中,string是一种通常类似于值类型的引用类型。它在堆上分配(不像大多数值类型那样堆栈),并具有自动引用计数和写时复制语义。
要了解这意味着什么,请考虑正常值类型,例如:一个整数,当作为参数传递给过程时表现:
var
gValue: Integer;
procedure PassByValue(aValue: Integer);
begin
// Here @gValue <> @aValue
aValue := aValue + 2;
// Here @gValue <> @aValue
end;
procedure PassByRefrenceInOut(var aValue: Integer);
begin
// Here @gValue = @aValue
aValue := aValue + 2;
// Here @gValue = @aValue
end;
procedure CallProcedures;
begin
gValue := 0;
PassByValue(gValue);
// gValue is still 0
PassByReferenceInOut(gValue);
// gValue is 2
end;
PassByReferenceInOut中的var参数等效于将指针传递给参数的C约定。
相同的语义适用于字符串参数传递,但值的内部表示存在细微差别:
var
gValue: string;
procedure PassByValue(aValue: string);
begin
// Here PChar(gValue) = PChar(aValue) <<<<
aValue := aValue + '2';
// Here PChar(gValue) <> PChar(aValue)
end;
procedure PassByRefrenceInOut(var aValue: string);
begin
// Here PChar(gValue) = PChar(aValue)
aValue := aValue + '2';
// Here PChar(gValue) = PChar(aValue)
end;
procedure CallProcedures;
begin
gValue := '';
PassByValue(gValue);
// gValue is still ''
PassByReferenceInOut(gValue);
// gValue is '2'
end;
如果要确保过程在其自己的字符串值副本上运行,请使用UniqueString过程,例如:
procedure PassByValue(aValue: string);
begin
// Here PChar(gValue) = PChar(aValue)
UniqueString(aValue);
// Here PChar(gValue) <> PChar(aValue)
aValue := aValue + '2';
// Here PChar(gValue) <> PChar(aValue)
end;
答案 2 :(得分:9)
在Delphi中通过引用传递显式添加var关键字:
procedure myFunc(var mytext:String);
这意味着myFunc可以修改字符串的内容并让调用者看到更改。