次に何が起こっても、プログラムが未定義の動作を引き起こす場合、未定義の動作が発生します。ただし、次の例を示しました。
int num = ReadNumberFromConsole();
if (num == 3) {
PrintToConsole(num);
*((char*)NULL) = 0; //undefined behavior
}
コンパイラが の定義を認識しない限り、条件PrintToConsole
を削除することはできません。の次の宣言を持つシステムヘッダーがif (num == 3)
あると仮定しましょう。LongAndCamelCaseStdio.h
PrintToConsole
void PrintToConsole(int);
あまり役に立ちません。では、この関数の実際の定義を確認することで、ベンダーがどれほど悪質であるか (または、それほど悪くなく、未定義の動作がさらに悪かった可能性があります) を見てみましょう。
int printf(const char *, ...);
void exit(int);
void PrintToConsole(int num) {
printf("%d\n", num);
exit(0);
}
実際、コンパイラは、コンパイラが何をするかわからない任意の関数が終了するか、例外をスローする可能性があると想定する必要があります (C++ の場合)。実行は呼び出し*((char*)NULL) = 0;
後に続行されないため、実行されないことに気付くでしょう。PrintToConsole
PrintToConsole
実際に戻ると、未定義の動作が発生します。コンパイラは、これが発生しないことを期待しています (これにより、プログラムが未定義の動作を何があっても実行する可能性があるため)、したがって、何でも発生する可能性があります。
ただし、別のことを考えてみましょう。null チェックを行っているとしましょう。null チェック後に変数を使用します。
int putchar(int);
const char *warning;
void lol_null_check(const char *pointer) {
if (!pointer) {
warning = "pointer is null";
}
putchar(*pointer);
}
この場合、lol_null_check
NULL 以外のポインターが必要であることが容易にわかります。グローバルな不揮発性warning
変数への割り当ては、プログラムを終了したり、例外をスローしたりするものではありません。も不揮発性であるため、関数のpointer
途中で値を魔法のように変更することはできません (変更した場合、未定義の動作になります)。呼び出すlol_null_check(NULL)
と未定義の動作が発生し、変数が割り当てられない可能性があります (この時点で、プログラムが未定義の動作を実行することがわかっているため)。
ただし、未定義の動作は、プログラムが何でもできることを意味します。したがって、未定義の動作が時間をさかのぼり、int main()
実行の最初の行の前にプログラムがクラッシュするのを止めるものは何もありません。これは未定義の動作であり、意味を成す必要はありません。3 を入力した後にクラッシュする可能性もありますが、未定義の動作は時間をさかのぼり、3 を入力する前にクラッシュします。そして、おそらく未定義の動作がシステム RAM を上書きし、2 週間後にシステムがクラッシュする可能性があります。未定義のプログラムが実行されていない間。