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