代码之家  ›  专栏  ›  技术社区  ›  Jim McKeeth

如何在Delphi中泄漏字符串

  •  10
  • Jim McKeeth  · 技术社区  · 15 年前

    5 回复  |  直到 15 年前
        1
  •  8
  •   mghie    15 年前

    我不知道你第二段中的问题,但有一次我被记录中泄露的字符串咬到了。

    如果你打电话 FillChar() 在包含字符串的记录上,用零覆盖动态分配内存的引用计数和地址。除非字符串为空,否则将泄漏内存。解决这个问题的办法是打电话 在清除记录占用的内存之前,先对其进行备份。

    不幸的是 当没有需要终结的记录成员时,会导致编译器提示。我碰巧把那封信注释掉了 定稿 调用以使提示静音,但后来当我向记录中添加字符串成员时,我没有取消对调用的注释,因此引入了泄漏。幸运的是,在调试模式下,我通常在最详细和最偏执的设置中使用FastMM内存管理器,因此泄漏不会被忽略。

    定稿 如果不需要的话打电话给IMHO会更好。

        2
  •  4
  •   Community CDub    7 年前

    不,我认为这样的事情不会发生。字符串变量有可能获得您不期望的值,但不会泄漏内存。考虑一下:

    var
      Global: string;
    
    procedure One(const Arg: string);
    begin
      Global := '';
    
      // Oops. This is an invalid reference now. Arg points to
      // what Global used to refer to, which isn't there anymore.
      writeln(Arg);
    end;
    
    procedure Two;
    begin
      Global := 'foo';
      UniqueString(Global);
      One(Global);
      Assert(Global = 'foo', 'Uh-oh. The argument isn''t really const?');
    end;
    

    One 的参数被声明为常量,所以据推测,它不会改变。但是 通过更改实际参数而不是形式参数来避免这种情况。程序 Two “知道”这一点 的参数是常量,因此它希望实际参数保留其原始值。断言失败了。

    字符串没有泄漏,但此代码确实演示了如何获取 悬挂参考 为了一根绳子。 Arg 是的本地别名 Global . 虽然我们变了 全球的 , Arg 的值保持不变,并且因为它被声明为const,所以字符串的引用计数在进入函数时不会增加。再赋值 全球的 Arg UniqueString 只是为了确保字符串被引用计数。否则,它可能是非引用计数的字符串文字。)所有编译器管理的类型都容易出现此问题;简单类型是免疫的。

    泄漏字符串的唯一方法是将其视为字符串以外的内容,或者使用非类型感知的内存管理函数。 Mghie's answer 描述如何使用 FillChar GetMem FreeMem . 例如:

    type
      PRec = ^TRec;
      TRec = record
        field: string;
      end;
    
    var
      Rec: PRec;
    begin
      GetMem(Rec, SizeOf(Rec^));
      // Oops. Rec^ is uninitialized. This assignment isn't safe.
      Rec^.field := IntToStr(4);
      // Even if the assignment were OK, FreeMem would leak the string.
      FreeMem(Rec);
    end;
    

    Initialize Finalize

    GetMem(Rec, SizeOf(Rec^));
    Initialize(Rec^);
    Rec^.field := IntToStr(4);
    Finalize(Rec^);
    FreeMem(Rec);
    

    另一种是使用类型感知功能:

    New(Rec);
    Rec^.field := IntToStr(4);
    Dispose(Rec);
    
        3
  •  2
  •   Jim McKeeth    15 年前

    实际上,在Delphi2007和2009中,作为常量或非常量传递字符串在引用计数方面是相同的。当字符串作为常量传递时,会导致访问冲突。这是一个问题

    type
      TFoo = class
        S: string;
        procedure Foo(const S1: string);
      end;
    
    procedure TFoo.Foo(const S1: string);
    begin
      S:= S1; //access violation
    end;
    
    var
      F: TFoo;
    begin
      F:= TFoo.create;
      try
        F.S := 'S';
        F.Foo(F.S);
      finally
        F.Free;
      end;
    end.
    
        4
  •  1
  •   Community CDub    7 年前

    泄漏字符串的另一种方法是将其声明为threadvar变量。看见 my question 详情请参阅。有关解决方案,请参见 the solution on how to tidy it

        5
  •  0
  •   Jim McKeeth    15 年前

    我想 this 可能和我想的差不多。它与字符串泄漏相反,字符串很早就被收集:

    var
      p : ^String;
    
    procedure InitString;
    var
      s, x : String;
    begin
      s := 'A cool string!';
      x := s + '. Append something to make a copy in' +
                 'memory and generate a new string.';
    
      p := @x;
    end;
    
    begin
      { Call a function that will generate a string }
      InitString();
    
      { Write the value of the string (pointed to by p) }
      WriteLn(p^); // Runtime error 105!
    
    
      { Wait for a key press }
      ReadLn;
    end.