未定義の動作は、次に何が起こってもプログラムが未定義の動作を引き起こす場合に発生します。ただし、次の例を示しました。
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週間後にシステムをクラッシュさせるでしょう。未定義のプログラムが実行されていない間。