Cでは、いくつかの式は未定義の動作をもたらします 。標準では、コンパイラがそのような式に遭遇した場合の動作を定義しないことを明示的に選択します。結果として、コンパイラは、見た目に合ったものを自由に実行することができ、有用な結果、予期しない結果、またはクラッシュさえ引き起こす可能性があります。
UBを呼び出すコードは、特定のコンパイラを使用して特定のシステムで意図されたとおりに動作する可能性がありますが、別のシステムや別のコンパイラ、コンパイラのバージョンまたはコンパイラの設定では機能しない可能性があります。
未定義行動(UB)とは何ですか?
未定義の動作は、C標準で使用される用語です。 C11標準(ISO / IEC 9899:2011)では、定義されていない動作という用語を
移植性のないまたは誤ったプログラム構成の使用時または誤ったデータの使用時に、この国際規格は要求を課さない
私のコードにUBがあるとどうなりますか?
これらは、標準に従った未定義の振る舞いのために起こり得る結果です:
注記未定義の動作の可能性は、予測不可能な結果を完全に無視して状況を無視すること、環境の文書化された方法で翻訳またはプログラムの実行中に(診断メッセージの発行の有無にかかわらず)診断メッセージの発行)。
次の引用符は、定義されていない動作から生じる結果(形式的ではありませんが)を記述するためによく使用されます。
コンパイラがANSI C標準に違反することなくコードを解釈するために任意の奇妙な方法を選択する可能性があることを暗示しています)
なぜUBは存在するのですか?
それが悪いのであれば、なぜ単に定義するのか、それとも実装定義にするのでしょうか?
未定義の動作により、最適化の機会が増えます。コンパイラは、すべてのコードに未定義のビヘイビアが含まれていないと仮定することができます。実行時のチェックを避け、有効性が高価または不可能な最適化を実行できます。
なぜUBは追跡が難しいのですか?
定義されていない動作が検出しにくいバグを作成する理由は少なくとも2つあります。
NULLポインタの参照を考慮してください。コンパイラは、NULLポインタの逆参照を診断する必要はなく、実行時に関数に渡されたポインタやグローバル変数内でNULLが存在する可能性もありません。 また、ヌルポインタ逆参照が発生すると、標準ではプログラムがクラッシュする必要があるとは限りません。むしろ、プログラムが早期にクラッシュしたり、後でクラッシュしたり、クラッシュしたりすることはありません。まるでヌルポインタが有効なオブジェクトを指し示し、完全に正常に動作し、他の状況下ではクラッシュするように動作する可能性があります。
nullポインタの逆参照の場合、C言語はnullポインタの参照の動作が定義されているJavaやC#などの管理言語とは異なります。正確な時刻に例外がスローされます(JavaではNullPointerException
、CではNullReferenceException
)したがって、JavaまたはC#からのメッセージは、診断メッセージの発行の有無にかかわらず、Cプログラムがクラッシュしなければならないと誤って信じている可能性があります 。
追加情報
このような状況を明確に区別する必要があります。
また、コンパイラやライブラリの実装者が独自の定義を出す余地を残すために、C標準では意図的に特定の構文の動作が意図的に定義されていないことが多くの場所で認識されています。良い例はシグナルとシグナルハンドラです。POSIXオペレーティングシステムの標準のような、Cへの拡張がはるかに精巧なルールを定義しています。そのような場合は、プラットフォームのドキュメントをチェックするだけです。 Cの標準はあなたに何も伝えることができません。
プログラムで未定義の動作が発生した場合、未定義のビヘイビアが発生した点だけが問題になり、プログラム全体が無意味になることを意味しないことにも注意してください。
このような懸念があるため、Cでのプログラミングの人にとっては、少なくとも未定義の動作を引き起こすようなことに精通していることが重要です(特に、コンパイラはUBについて常に警告しているわけではないため)。
未定義の動作を検出するのに役立ついくつかのツール(例えば、PC-Lintなどの静的解析ツール)がありますが、未定義の動作のすべての発生を検出することはできません。