Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/delphi/8.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
String 为什么程序在使用常量字符串参数时崩溃?_String_Delphi_Memory_Crash_Constants - Fatal编程技术网

String 为什么程序在使用常量字符串参数时崩溃?

String 为什么程序在使用常量字符串参数时崩溃?,string,delphi,memory,crash,constants,String,Delphi,Memory,Crash,Constants,我的程序有以下代码: function FooBar(const s: string): string; var sa: AnsiString; begin // .......................... sa := AnsiString(s); sa := AnsiString(StringReplace(string(sa), '*', '=', [rfReplaceAll])); sa := AnsiString(StringReplace(string

我的程序有以下代码:

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参数会使程序崩溃。我很想了解它


更新:该程序只在Delphi6中崩溃,并且只有在启用优化时才会崩溃。如果优化关闭,程序将正常工作。可能是Delphi错误吗?

因为AnsiString是指针。因此,您不能将其作为普通字符串进行操作(这意味着一个字符数组)。崩溃是随机的,因此您可以随时因堆栈溢出或访问冲突而获得崩溃,而不依赖于优化

如果函数FooBar返回结果,则sa变量已从mamory中释放


尝试使用PChar或PAnsiChar,并根据您的需要为这些变量分配内存。

由于转换为AnsiString并返回此处没有意义,我会将其重写为

function FooBar(const s:string):string;
begin
  Result:=
    StringReplace(
    StringReplace(
      s
      ,'*','=',[rfReplaceAll])
      ,' ','+',[rfReplaceAll]);
end;
在这种情况下:

 sa := s;
自动参考计数(ARC)工作。这是一种惯用的方式,编译器知道如何使用这些字符串-如果sa将更改,它将创建新的副本等等

对于硬类型铸造(尽管类型相同)

您告诉编译器您只想获取指向字符串的指针,并且您知道如何使用此字符串引用。编译器不会干扰和打扰您,但您有责任进行正确的操作


另外,我不能用Delphi XE5重现这个问题——简单赋值和类型转换都会导致使用ARC调用LStrLAsg(内部函数)。(当然,编译器的魔力可能会有所改变)

当涉及到常量字符串时,有一些特殊的问题。
许多年前,我帮助一位同事解决了一个类似的特殊问题(D3IIRC)。下面的简化示例看起来不像您的具体问题,但它可能会给您一些想法:

type
  TMyClass
    FString: string;
    procedure AppendString(const S: string);
  end;

procedure TMyClass.AppendString;
begin
  FString := FString + S;
end;
现在,如果您有一个
TMyClass
的实例,并尝试调用
AppendString(FString)
若要将字符串加倍,您可能会遇到访问冲突。(如果您这样做,还有一些其他因素可能会影响。)原因如下:

  • const防止在方法调用中重新计算字符串
  • 因此,当其值更改时,
    FString
    可能具有
    refCount=1
  • 在这种情况下,写入时复制不适用,字符串被重新分配。(很可能在不同的地址。)
  • 因此,当该方法返回时,S引用的地址无效,并触发AV

我们今天调试了一个崩溃,它是一个Delphi 5编译器代码gen错误:

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编译器错误地试图减少这两个函数的引用计数

  • 标题
  • s
并使
常量
字符串的引用计数变为零。这将导致它被释放。因此,当
DoSomething
返回时,您使用的是一个释放的字符串

正在等待发生的访问冲突


或者,在客户的情况下:发生。

您可以在没有sa的情况下尝试如下:
Result:=AnsiString(s);Result:=AnsiString(StringReplace(string(Result))
..?为什么在Delphi6中使用这些转换,默认情况下string=AnsiString?因为我正在编写一个必须由Delphi 6和XE4使用的组件。我不希望代码中有任何警告,所以我显式地将它们转换为强制类型(该函数只执行Base64操作,因此强制转换是正常的,不会影响Unicode功能)。只需使用字符串。此处不需要任何转换。StringReplace将起作用。如果您从此函数中删除所有类型转换和所有提及的
ansisting
,则不会收到任何隐式转换警告,因为不会有任何隐式转换。我了解到Pascal字符串使用自动引用计数,因此我不关心分配/释放内存。Delphi 6中的“字符串”和“AnsiString”之间有什么不同吗?我认为它们是相等的。您使用Delphi 6还是Delphi XE6?是的。但我的问题是,要理解为什么“sa:=s”resp.“const”程序崩溃。为什么自动引用计数不起作用?因为优化与分配给本地变量和强制转换混淆了。@StijnSanders:我认为分配给本地变量和强制转换并不重要。我认为这只是D6中的一个错误。非常感谢您提供的这一有用提示。那么,我理解了吗rect,则“sa:=AnsiString(s)”将使ARC计数器保留为1(无论s是定义为“const s”还是仅定义为“s”),并且由于局部变量“sa”随后被清除,因此s也将被清除(因为它们位于同一地址),这将导致调用方非法引用s?ISTM,而这只是D6中的一个bug。它在当前版本中不再存在。我查看了XE4中的代码,发现这是正确处理的。我不认为强制转换是问题所在。编译器应该能够处理(或忽略)这些。我在某个地方读到了“const”在较新的Delphi版本中失去了他的功能(请参阅Andreas Hausladden的第二条评论)。这可能是它在较新版本上工作的原因。我将此答案作为解决方案屏蔽,尽管其他答案也正确,因为我在原始帖子中的代码有许多不同的问题(例如,返回局部变量)。但是,您提到了一个我不知道的非常有趣的事实(typecast不会增加ARC计数器)。当前版本的_UStrCat(在附加字符串时调用的RTL例程)知道这一点,并且不会使用错误的地址。我周围没有D6,因此可能是它尚未检查这一点。
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;