我的程序有以下代码:
function FooBar(const s: string): string;
var
sa: AnsiString;
begin
// ..........................
sa := AnsiString(s);
sa := AnsiString(StringReplace(string(sa), '*', '=', [rfReplaceAll]));
sa := AnsiString(StringReplace(string(sa), ' ', '+', [rfReplaceAll]));
result := string(sa);
// ..........................
end;
我注意到程序确实在“某个地方”崩溃了,FastMM4说我写了一个释放的对象。一旦我注释掉了“const”,程序就会起作用。
我已阅读有关const参数的Delphi文档,但我无法弄清楚为什么const参数会崩溃程序。我很乐意理解它。
更新:该程序仅在Delphi 6中崩溃且仅在优化为ON时才会崩溃。如果优化为OFF,程序将正常工作。它可能是Delphi的错误吗?
答案 0 :(得分:3)
对于 const string 参数,有一些奇怪的问题。
很多年前,我帮助一位同事解决了类似的特殊问题(D3 iirc)。以下简化示例看起来不像您的具体问题,但它可能会给您一些想法:
type
TMyClass
FString: string;
procedure AppendString(const S: string);
end;
procedure TMyClass.AppendString;
begin
FString := FString + S;
end;
现在,如果您有TMyClass
的实例并尝试调用AppendString(FString);
来加倍字符串,则可能会出现访问冲突。 (如果你这样做,还有一些其他因素会影响。)原因如下:
FString
的值可能会更改refCount = 1
。答案 1 :(得分:2)
对于这种情况:
sa := s;
自动参考计数(ARC)有效。这是惯用的方式,编译器知道如何使用这些字符串 - 如果sa将改变,它会创建新的副本,依此类推。
对于硬类型转换的情况(虽然类型相同)
sa := AnsiString(s);
你告诉编译器你只想获得指向字符串的指针,你知道如何使用这个字符串引用。编译器不会干扰和骚扰您,但您负责正确的操作
P.S。我无法用Delphi XE5重现这个问题 - 简单的赋值和类型转换会导致LS调用LStrLAsg(内部函数)。 (当然,编译器魔法可能会有所改变)
答案 2 :(得分:2)
我们今天调试了一个Delphi 5编译器代码错误的崩溃:
procedure TForm1.Button1Click(Sender: TObject);
var
s: string;
begin
s := 'Hello, world! '+IntToStr(7);
DoSomething(s);
//String s now has a reference count of zero, and has already been freed
ShowMessage(s);
end;
procedure TForm1.DoSomething(const Title: string);
var
s: AnsiString;
begin
s := AnsiString(Title);
if Now = 7 then
OutputDebugString('This is a string that is irrelevant');
end;
Delphi 5编译器错误地尝试减少两者的引用计数
Title
s
并使const
字符串的引用计数变为零。这导致它被释放。因此,当DoSomething
返回时,您正在使用释放的字符串。
等待发生的访问冲突。
或者,就客户而言:正在发生。
答案 3 :(得分:0)
因为AnsiString是一个指针。所以,你不能把它作为一个通常的字符串(这意味着一系列字符)来操作。崩溃是随机的,因此您可以随时获得堆栈溢出或访问冲突的结果,而不是依赖于优化。
比你的函数FooBar返回结果,一个变量已经从mamory中释放。
尝试使用PChar或PAnsiChar并根据需要为这些变量分配内存。
答案 4 :(得分:0)
由于转换为AnsiString并返回此处没有意义,我会将其重写为
function FooBar(const s:string):string;
begin
Result:=
StringReplace(
StringReplace(
s
,'*','=',[rfReplaceAll])
,' ','+',[rfReplaceAll]);
end;