【问题标题】:Premature string destruction and how to avoid it?过早的字符串破坏以及如何避免它?
【发布时间】:2010-12-04 05:32:45
【问题描述】:

我正在使用 Delphi 2009,但使用以下代码段时出现了一些奇怪的错误:

var
  Str     : AnsiString;
  CharPtr : PAnsiChar;
...
CharPtr := PAnsiChar (Str);
ExecuteInBackgroundThread (
  procedure
  begin
  DoSomething (CharPtr);
  end);

我猜想字符串在超出范围时会被破坏,并且在某些时间条件下 DoSomething 会产生最奇怪的结果。所以第一个问题是:我说的对吗?

第二个问题是:如何规避被破坏的字符串?这样做的正确方法是什么?

提前致谢。

【问题讨论】:

    标签: multithreading delphi delphi-2009 anonymous-methods


    【解决方案1】:

    所以第一个问题是:我说的对吗?

    很可能,是的。 Delphi 的 AnsiString 是引用计数的。当Str 超出范围时,引用计数会减少。如果引用计数达到零,那么它所占用的内存可能会被重用。

    第二个问题是:我怎样才能 规避字符串是 破坏?什么是正确的方法 到这个?

    不使用指针,像这样:

    var
      Str     : AnsiString;
    ...
    ExecuteInBackgroundThread (
      procedure
      begin
      DoSomething (Str);
      end);
    

    【讨论】:

      【解决方案2】:

      只需使用:

      DoSomething(PAnsiChar(Str));
      

      一般规则很简单:直到最后一刻才使用 PChar。这样您就无需过多考虑内存管理问题(嗯,大部分情况下)。

      另见this great article。

      【讨论】:

      • 这对真正的问题有什么好处——字符串超出范围?
      • 那是因为你在匿名方法中使用了 Str,所以现在字符串变量将被捕获并且不会超出范围;)
      【解决方案3】:

      好吧,我想我可能已经想通了。

      我正在使用匿名方法,因此编译器应该捕获我的局部变量。显然,它只捕获了我在匿名方法中实际使用的变量。这意味着捕获了 CharPtr,但没有捕获 SendStr。因此,当 SendStr 超出范围时,它会被破坏,并且 CharPtr 现在有指向一些随机垃圾的危险。

      经过以下修改

      ExecuteInBackgroundThread (
        procedure
        begin
        Log (Str);
        DoSomething (CharPtr);
        end);
      

      一切似乎都很好。

      【讨论】:

      • Str 引用计数可能会在 Log 语句之后而不是在过程结束时减少。或许不是,但关键是你不想依赖这样的编译器细节。
      • 我不太清楚,但我认为捕获的变量不会在匿名方法结束之前释放。
      • wcoenen - 是的,您可以依赖这些细节,否则 Delphi 代码会严重损坏 - 有很多代码将字符串类型转换为 PChar 以进行 API 调用,依赖于字符串不会消失,即使以后不使用了。
      • 粉碎机 - 你有正确的想法。捕获变量可以延长它们的生命周期。如果您不捕获字符串,但确实捕获了指向字符串内容的指针,则字符串将从您下方释放出来。因此,您可以使用无操作过程捕获字符串 - 例如“procedure Use(const X); begin end;” - 或者您将类型转换留到最后一分钟,或者在匿名方法的主体中进行。
      【解决方案4】:

      为什么不按值传递字符串,而不是指针/引用?

      【讨论】:

      • 粉碎机,见 wcoenen 的回答。
      • @Rob Kennedy:这不是真正的价值,不是吗?重要的区别是 Str 在匿名方法中使用,因此会被捕获。 Delphi 中的字符串和“按值”对我来说听起来很矛盾。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-02-29
      • 2019-05-19
      • 2011-07-10
      • 1970-01-01
      • 2011-09-26
      • 1970-01-01
      相关资源
      最近更新 更多