**未定義動作(Undefined Behavior, UB)**とは、言語の標準がプログラムの動作を定義していない状況です。コンパイラはプログラムに対して何をしても良く、プログラムはクラッシュ、誤った結果を返す、または「正常に動作する」可能性があります。UBは、配列の境界を越えたアクセス、NULLポインタの逆参照などのエラーから発生します。
int arr[5]; arr[10] = 42; // UB: 配列の境界を越えたアクセス int* p = nullptr; *p = 1; // UB: NULLの逆参照
UBを回避するには、標準に従い、現代のツール(ASan、UBSan、valgrind)を使用し、生ポインタをあまり使用せず、安全なコードを書くよう努めることが重要です。
プログラムのある部分でUBが発生した場合、全く別の「独立した」コード部分に影響を与える可能性がありますか?
はい!コンパイラは最適化中に予期しない変換を行うことがあり、UBが発生した場合、それが検出されればそうなります。
void foo(int* p) { if (p == nullptr) return; *p = 5; // もしpがnullptrでなかった場合、これはUBです!しかし、コンパイラはpが常に有効であると考えてチェックを省略する可能性があります。 }
物語
大企業のサーバーでNULLポインタの逆参照により、時折ランダムなプロセスのクラッシュが発生し、再現が難しかった:デバッグモードではすべてが正常に動作したが、リリースモードでは動作しなかった。
物語
32ビットから64ビットへのコード移植時にデータ型を間違え、intとポインタの間でキャストを使用した結果、一部のマシンでは正常に動作したが、他のマシンではクラッシュや奇妙なアーティファクトが発生した。
物語
インターネットでは、無害なUB(配列の境界を越えたアクセス)がプログラム全体の動作を壊す事例が知られている:コンパイラは配列に関する操作だけでなく、エラーとは無関係なコード部分も「最適化」して削除した。