2010-11-22 7 views
7

は、次のC++のプログラムを考えてみましょうメモリーリークの可能性使用してC++文字列

==1643== HEAP SUMMARY: 
==1643==  in use at exit: 26 bytes in 1 blocks 
==1643== total heap usage: 1 allocs, 0 frees, 26 bytes allocated 
==1643== 
==1643== LEAK SUMMARY: 
==1643== definitely lost: 0 bytes in 0 blocks 
==1643== indirectly lost: 0 bytes in 0 blocks 
==1643==  possibly lost: 26 bytes in 1 blocks 
==1643== still reachable: 0 bytes in 0 blocks 
==1643==   suppressed: 0 bytes in 0 blocks 

あなたが見ることができるように、ありますヒープ上に割り当てられた26バイトが失われました。私はstd::stringクラスが12バイトの構造体(少なくとも私の32ビットx86アーチとGNUコンパイラ4.2.4では)と "Hello、World!"を持っていることを知っています。ヌルターミネータは14バイトです。私が正しく理解していれば、12バイト構造体には文字列へのポインタ、割り当てられたサイズ、および参照カウントが含まれています。

私の質問:C++の文字列はスタック/ヒープに関してどのように格納されていますか?宣言されたときにstd::string(または他のSTLコンテナ)のスタックオブジェクトが存在しますか?

P.S.私はどこかvalgrind かもしれないは、STLコンテナ(および "ほとんどコンテナ"、例えばstd::string)を使用するいくつかのC++プログラムで、メモリリークの偽陽性を報告する可能性があります。私はこのリークについてあまり心配していませんが、STLコンテナとメモリ管理に関して私の好奇心を誇示しています。

+1

なぜあなたはdie()を呼び出していますか? – EboMike

+2

@EboMike: 'exit(0)'呼び出しを 'main()'から分離する 'die()'関数を書いています。私のプログラムは現在、このプログラムの実行時に 'exit(0)'関数が「プラグをプルする」関数に制御を移す必要があります。このプログラムは、学問的理由以外の有用な目的を果たしていないことに留意してください。 – pr1268

+4

私のポイントは、最初の場所でdie()がリークを引き起こすということです。あなたがそこにdie()を持っていることを嫌っているなら、少なくとも文字列をそれ自身のスコープに入れてください。 – EboMike

答えて

7

他は正しいですが、あなたはexitを呼び出しているため、漏れています。クリアするために、リークはスタックに割り当てられた文字列ではなく、文字列によってヒープに割り当てられたメモリです。例:

valgrindはリークを報告しません。

ヒープ上に割り当てられたメモリへの内部ポインタがあるため、リークは可能です(確定的ではありません)。 basic_stringはこれを担当します。私のマシン上のヘッダから:

* A string looks like this: 
    * 
    * @code 
    *          [_Rep] 
    *          _M_length 
    * [basic_string<char_type>]   _M_capacity 
    * _M_dataplus       _M_refcount 
    * _M_p ---------------->    unnamed array of char_type 
    * @endcode 
    * 
    * Where the _M_p points to the first character in the string, and 
    * you cast it to a pointer-to-_Rep and subtract 1 to get a 
    * pointer to the header. 

彼らはキー_M_pは、ヒープ上に割り当てられたメモリの先頭を指していないということです、それは文字列の最初の文字を指します。ここに簡単な例があります:

struct Foo 
{ 
    Foo() 
    { 
     // Allocate 4 ints. 
     m_data = new int[4]; 
     // Move the pointer. 
     ++m_data; 
     // Null the pointer 
     //m_data = 0; 
    } 
    ~Foo() 
    { 
     // Put the pointer back, then delete it. 
     --m_data; 
     delete [] m_data; 
    } 
    int* m_data; 
}; 

int main() 
{ 
    Foo f; 
    die(); 
} 

これはvalgrindの可能性のあるリークを報告します。私がm_dataを動かす行をコメントアウトすると、valgrindは 'まだ到達可能です'と報告します。 m_dataを0に設定した行のコメントを外すと、明確なリークが発生します。

valgrind documentationには、リークと内部ポインターの可能性に関する情報があります。

+0

ありがとうございます。これは、C++文字列とそれらの割り当てについて多くのことを説明しています。 – pr1268

1

gcc STLには、コンテナと文字列用のプライベートメモリプールがあります。これをオフにすることができます。 s sが実行する機会をデストラクタのスタックフレームは、あなたがs与えていないままにする "を前exit INGによって、この「リーク」valgrindのよくある質問にもちろん

http://valgrind.org/docs/manual/faq.html#faq.reports

+0

何年も前のことですが、GCCの標準ライブラリの標準ライブラリでは、メモリプールは使用されていません。 –

4

を見て。

質問:wrt std::stringストレージ:実装によって実装方法が異なります。文字列が12バイト以下の場合に使用されるスタックには、約12バイトが割り当てられます。長い文字列はヒープに移動します。他の実装は常にヒープに行きます。いくつかは参照カウントされ、コピーオンライトセマンティクスを使用します。 Scott MeyersのEffective STL、アイテム15を参照してください。

1

私はexit()を使わないでください。私はその呼び出しを使用する本当の理由は見当たりません。 valgrindはまだ実行されているように見えますが、最初にメモリをクリーンアップせずにプロセスを即座に停止させるかどうかはわかりません。

11

exit」を呼び出すと、現在のブロックを残さずにプログラムが終了するため、自動保存期間のあるオブジェクトが破棄されることはありません。

つまり、リークかどうか、本当に気にする必要はありません。 exitに電話すると、「このプログラムを閉じて、もう何も気にしない」と言っています。だから心配しないでください。 :)

もちろん、リソースの管理方法にかかわらず、ストリングのデストラクタを実行させることは決してないので、リソースがリークすることは明らかです。

+0

はマルチスレッドに関連していますが、 'exit'が子スレッドから呼び出された場合、または呼び出されたスレッドのみから呼び出された場合は、プロセス全体が停止しますか? –

+0

@Matt:私が読むことができる限り、それは他のスレッドが存在しないかのように動作し、同じことをします。つまり、スレッドの局所統計がクリーンアップされ、プログラムの統計などが返されます。 – GManNickG

関連する問題