2009-07-15 9 views
10

先日、デルファイで文字列をリークさせる方法について、同僚と話していました。デフォルトでは、文字列は参照カウントされ、自動的に割り当てられるので、通常は何も考慮せずに手動で割り当て、サイズの計算、またはメモリ管理をする必要はありません。Delphiで文字列をリークする方法

しかし、私は一度、文字列をリークする方法があることを覚えています(リークしたオブジェクトに含めることなく)。それは、参照によって文字列を渡し、それが渡されたルーチン内から大きなスコープからアクセスすることと関係があるようです。ええ、私はそれがあいまいであることを知っています、それが私がここで質問している理由です。

答えて

2

実は、CONSTまたは非constのとして文字列を渡すことで参照カウントの期間に同じですDelphi 2007 and 2009.文字列がCONSTとして渡されたときにアクセス違反が発生するケースがありました。ここで私はthisは私が考えていたものに似ているかもしれないと思う問題1

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. 
+0

私は思っていたように見えますが、2009年にはAVではありません。私はあなたが言っていたことを信じています。 –

+3

$ STRINGCHECKSがONの場合、Delphi 2009では "const"の機能が失われるため、AVはありません。 –

7

2番目の段落の問題についてはわかりませんが、レコードで漏れた文字列で1回噛まれました。

文字列を含むレコードでFillChar()を呼び出すと、refカウントと動的に割り当てられたメモリのアドレスが0で上書きされます。文字列が空でない限り、メモリがリークします。これを回避する方法は、占有するメモリをクリアする前に、Finalize()をレコードにコールすることです。

残念なことに、を呼び出すFinalize()ファイナライズが必要なレコードメンバーがない場合、コンパイラのヒントが発生します。 Finalize()コールでヒントを無音にしたが、後でレコードに文字列メンバーを追加したときに、コールのコメントを忘れてしまったので、リークが発生した。幸運にも、私は一般的にFastMMメモリマネージャをデバッグモードで最も冗長で妄想的な設定で使用しているので、漏れは気付かれませんでした。

Finalize()コールが不要な場合は、コンパイラのヒントはそれほど良いことではないでしょうが、はるかに良いIMHOです。

+0

>必要がない場合は、Finalize()コールを黙って省略してください。IMHO +1 –

+0

短い文字列がこの影響を受けるかどうか知っていますか? –

+0

@JamesB:いいえ、短い文字列は参照カウントではなく、失われたメモリのチャンクへのポインタで構成されていないため、これの影響を受けません。短い文字列は単一の長さのバイトと文字データで構成されているので、 '0 'でそれらを埋めれば' 0'に長さが設定され、 '#0'にすべての要素が設定されます。 – mghie

4

いいえ、私はそんなことは起こり得ないと思います。文字列変数は期待していない値を取得する可能性がありますが、メモリがリークすることはありません。このことを考えてみましょう:

ここ
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の引数はそうおそらく、constとして宣言され、それは変更されません。しかし、Oneは、仮パラメータの代わりに実際のパラメータを変更することで回避します。プロシージャTwoは、Oneの引数がconstであることを "知っている"ので、実際のパラメータが元の値を保持することを期待しています。アサーションは失敗します。

文字列はリークしませんでしたが、このコードでは、ダングリングリファレンスを文字列として取得する方法を示しています。 Argは、ローカル別名Globalです。 Globalを変更しましたが、Argの値は変更されず、constとして宣言されているため、関数への入力時に文字列の参照カウントがインクリメントされませんでした。再割り当てGlobalは、参照カウントをゼロに落とし、その文字列は破棄されました。 varとして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; 

2つの方法で修正できます。一つはInitializeFinalizeを呼び出すことです:

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

他のタイプの認識機能を使用することです:

New(Rec); 
Rec^.field := IntToStr(4); 
Dispose(Rec); 
+1

AFAICSグローバルサンプルはどのような変数型でも動作します。 –

+1

ああ、そうです。しかし、文字列として、この答えを編集するときに実証しようとしているので、追加の問題を引き起こす可能性があります。 –

0

です。文字列リークの逆で、早く収集される文字列です。

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. 
+3

スコープ外に出たスタック上のオブジェクトへのポインタがあります。あなたは何を期待していますか?文字列がクリーンアップされているかどうかは、これを壊す可能性があります。 –

+2

Loren、書いた実際のコードにかかわらず、あなたが意図した通りに動作することを期待しています!コンパイラは最近、霊的ですね。 –

+0

ここではLorenに同意する必要があります。 2番目の記号は@記号を呼び出します。すべてのベットはオフです。あなたは、コンパイラにあらゆる型安全性と参照カウントメカニズムへの礼儀正しいお別れと、生の記憶をあなた自身の手に渡すよう呼びかけています。 –

0

文字列をリークする別の方法は、threadvar変数として宣言することです。詳細は、my questionを参照してください。そして解決方法については、the solution on how to tidy itを参照してください。

関連する問題