2016-08-10 11 views
3

Valgrindは、ヒープ上の空きオブジェクトへの残存参照を検出するのに便利です。ただし、スタック上のスコープ外の変数への参照が長引いているとは限りません。例えば:C++でスコープ外のスタック変数への参照を検出するにはどうすればよいですか?

#include <iostream> 

struct CharHolder { 
    const char ch; 
    CharHolder(char _ch) : ch(_ch) {} 
}; 

struct Printer { 
    const CharHolder& ref; 
    Printer(const CharHolder& _ref) : ref(_ref) {} 
    void print() { 
     std::cout << &ref << ": " << ref.ch << std::endl; 
    } 
}; 

int main() { 
    // g++ -O0: prints 'x' 
    // g++ -O3: prints undefined character 
    Printer p1(CharHolder('x')); 
    p1.print(); 

    // g++: prints undefined character 
    CharHolder* h = new CharHolder('x'); 
    Printer p2(*h); 
    delete h; 
    p2.print(); 
} 

最初の例では、p1と、CharHolder('x')をできるだけ早くp1の構築が完了すると破壊されているので、プリンタが、外のスコープスタック変数への参照を保持しているものです。

p2を持つ2番目の例は、p2print()でそれを参照しようとする前に、フリーであるヒープ変数への参照をプリンタが保持している例です。

Valgrindのは、第二の例文句:

==82331== Invalid read of size 1 
==82331== at 0x400A8E: Printer::print() 
==82331== by 0x400967: main 
==82331== Address 0x5a1c040 is 0 bytes inside a block of size 1 free'd 
==82331== at 0x4C2C2BC: operator delete(void*) 
==82331== by 0x40095F: main 

どのようにして、おそらくValgrindのようなツールを使用して、第一種のエラーを検出することができますか?

答えて

3

静的解析ツールは完全ではありません。 valgrindのような静的解析ツールは、一般的なプログラミングバグを捕らえた素晴らしい実績があります。

しかし、それらを100%捕まえることはできません。

これらの種類のプログラミングバグを可能な限り避けようとする私のアプローチは、契約によって、これらの種類のプログラミングバグが論理的に不可能であることを証明することを目的とした防御プログラミング規律です。これには次のようなものが含まれます:

  1. 参照とポインタの代わりにスマートポインタを使用します。スマートポインタを使用すると、スコープ外に出るオブジェクトへの参照が論理的に不可能になることが、契約によって証明できます。

  2. 古典的なfor (size_t i=0; i<container.size(); ++i)アプローチの代わりにイテレータと標準ライブラリアルゴリズムを使用します。明確に定義された開始と終了で、配列の終わりを走るイテレータは論理的に不可能になります。さらに、特別なボーナスとして、何らかの理由でコンテナの選択が切り替えられた場合、コードの変更が少なくて済みます。

あなたのケースでは、実行時のみの静的解析ツールでこれを検出することはほとんど不可能です。最終的にコンパイルされたコードには、実行時に、一時的に一時的にスコープから外れるとマークするものは何もありません。生成されたコードは、自動スコープ変数とパラメータとして渡される一時変数の両方に対応するのに十分なスタックフレームを割り当てます。コンストラクタの呼び出しが完了した後、明示的な呼び出しは生成されず、一時的なものが破棄されたものとしてマークされます。私はvalgrind、または他の静的解析ツールがこれをどのように知っているのかわかりません。

一時的なクラスに明示的なデストラクタがある場合、理論的には、汎用静的解析ツールが、そのデストラクタが呼び出されることによってクラスインスタンスが破棄されたことを知ることができます。

しかし、これは完全な答えがないことを示しています。私が言及したプログラミングの実践さえ、問題の100%を防ぐことはできません。スマートポインタを使用するときに循環参照のように考慮する必要のある複雑さを導入することがあります。

+6

「valgrind」は静的解析ツールではありません。 – immibis

+0

私はそれは渡された関数の引数よりも長く続くどのような方法でも、渡された参照を格納することは悪い考えであるということは、オブジェクトレッスン(pun)を意図していると思います。戻ったときに無効なオブジェクトを参照し、そのメンバーは役に立たなくなります。このような状況に対して警告を生成するために、コンパイラを作成する必要があるかもしれません。 –

+0

@BlairHoughton - コンパイラが一時的な参照をパラメータとして渡す関数呼び出しをコンパイルするとき、コンパイラがコンパイルされていない可能性があるときに、呼び出された関数が渡された参照をどこかに格納することを、既知の記録された履歴の任意の時点で、呼び出された関数へのソースコードは、以前から始まっています。 –

関連する問題