仕様全体を記憶する以外に、プログラムが C++ (または C) で未定義の動作をしているかどうかを知る方法はありますか?
私が尋ねる理由は、プログラムがデバッグで動作しているが、未定義の動作が原因でリリースされていないという多くのケースに気付いたからです。少なくとも UB を特定するのに役立つツールがあればいいのですが、問題が発生する可能性があることはわかっています。
仕様全体を記憶する以外に、プログラムが C++ (または C) で未定義の動作をしているかどうかを知る方法はありますか?
私が尋ねる理由は、プログラムがデバッグで動作しているが、未定義の動作が原因でリリースされていないという多くのケースに気付いたからです。少なくとも UB を特定するのに役立つツールがあればいいのですが、問題が発生する可能性があることはわかっています。
優れたコーディング標準。あなた自身からあなたを守ります。ここにいくつかのアイデアがあります:
コードは最高の警告レベルでコンパイルする必要があります...警告なし。(つまり、最高レベルに設定されている場合、コードは警告をまったく発してはなりません。) すべてのプロジェクトでエラー オン警告フラグをオンにします。
これは、他の人のライブラリを使用する場合に余分な作業が必要になることを意味します。また、無意味な警告がいくつかあることもわかります...チームが決定したときに、それらを個別にオフにします。
常にRAIIを使用してください。
C スタイルのキャストは絶対に使用しないでください。 一度もない!- これを破らなければならないまれなケースがいくつかあると思いますが、おそらくそれらを見つけることはありません.
reinterpret_cast
キャストする必要がある場合void
は、ラッパーを使用して、常に同じ型との間でキャストするようにします。つまり、ポインター/オブジェクトを a でラップし、ポインターboost::any
を必要なものにキャストし、反対側でも同じことを行います。なんで?どのタイプから取得するかが常にわかりreinterpret_cast
、boost::any
その後正しいタイプにキャストしたことが強制されるためです。それはあなたが得ることができる最も安全です。
宣言の時点で(またはクラス内の場合はコンストラクター初期化子で)常に変数を初期化します。
他にもありますが、これらは非常に重要なものです。
誰も標準を覚えることはできません。中級から上級の C++ プログラマーが行うことは、安全であることがわかっている構造を使用し、人間の本性から身を守ることです...そして、安全でない構造は、必要がない限り使用しません。危険はすべて、地獄までテストされた安全なインターフェースに包まれています。
覚えておくべき重要なことの 1 つは、すべての言語で共通です。
コンストラクトを正しく使いやすく、間違って使いにくくする
すべてのケースで未定義の動作を検出できるわけではありません。たとえば、 を考えてみましょうx = x++ + 1;
。この言語に精通している場合は、それが UB であることを知っています。さて、*p = (*p)++ + 1;
もちろんUBもですが、どう*q = (*p)++ + 1;
でしょうか?それは UB の場合ですq == p
が、それ以外は定義されています (見た目が悪い場合)。p
与えられたプログラムでは、その行に到達したときにとq
が決して等しくないことを証明することは十分に可能かもしれませんが、それは一般的にはできません。
UB を見つけるには、手持ちのすべてのツールを使用してください。優れたコンパイラは、少なくともより明白なケースに対して警告を発しますが、最適なカバレッジのためにいくつかのコンパイラ オプションを使用する必要がある場合があります。さらに静的分析ツールがある場合は、それらを使用してください。
コード レビューは、このような問題を発見するのにも非常に役立ちます。複数の開発者が利用できる場合は、それらを使用してください。
ここでは、 PC-Lintなどの静的コード分析ツールが大いに役立ちます。
さて、この記事はほとんどの側面をカバーしています..
clang
未定義の動作に遭遇したときに警告を生成しようとします。
すべての形式の UB を検出するソフトウェア ツールを認識していません。明らかに、コンパイラの警告と、場合によっては lint または別の静的コード チェッカーを使用すると、大いに役立ちます。
もう 1 つの大きな助けになるのは、経験です。言語をプログラミングすればするほど、疑わしいと思われる構造が見えてきて、プロセスの早い段階でそれらを見つけることができます。
残念ながら、すべての UB を検出する方法はありません。それを行うには、停止問題を解決する必要があります。
あなたができる最善のことは、できるだけ多くのルールを知り、疑わしい場合はそれを調べ、他のプログラマーに確認することです (ペアプログラミング、コードレビュー、または単に SO の質問を通じて)
できるだけ多くの警告を表示してコンパイルし、複数のコンパイラでコンパイルすると役立つ場合があります。また、Valgrind などの静的分析ツールを使用してコードを実行すると、多くの問題を検出できます。
しかし、最終的には、すべてを検出できるツールはありません。
追加の問題は、多くのプログラムが実際に UB に依存しなければならないことです。一部の API はそれを必要とし、「すべての正常なコンパイラで動作する」と仮定します。OpenGL は、1 つまたは 2 つのケースでそれを行います。Win32 API は、標準準拠のコンパイラでさえコンパイルできません。
したがって、魔法の UB 検出ツールを持っていたとしても、制御できないケースによってつまずいてしまうことになります。
シンプル: できるかどうかわからないことはしないでください。
Intel C++ コンパイラなどの優れたコンパイラは、未定義の動作のケースの 99% を検出できるはずです。使用するフラグとスイッチを調査する必要があります。いつものように、マニュアルを読んでください。