一番の答えは間違った(しかし一般的な)誤解です:
未定義の動作は実行時のプロパティです*。それはできません「タイムトラベル」!
特定の操作は(標準で)副作用があると定義されており、最適化することはできません。I / Oを実行する操作、またはvolatile
変数にアクセスする操作は、このカテゴリに分類されます。
ただし、注意点があります。UBは、前の操作を元に戻す動作を含む、任意の動作である可能性があります。これは、場合によっては、以前のコードを最適化することと同様の結果をもたらす可能性があります。
実際、これはトップアンサーの引用と一致しています(私の強調):
整形式のプログラムを実行する適合実装は、同じプログラムと同じ入力を持つ抽象マシンの対応するインスタンスの可能な実行の1つと同じ観察可能な動作を生成するものとします。
ただし、そのような実行に未定義の操作が含まれている場合、この国際標準では、その入力を使用してそのプログラムを実行する実装に要件はありません(最初の未定義の操作に先行する操作に関しても)。
はい、この引用は「最初の未定義の操作に先行する操作に関してさえも」とは言っていませんが、これは特にコンパイルされているだけでなく、実行されているコードに関するものであることに注意してください。
結局のところ、実際に到達していない未定義の動作は何もしません。UBを含む行に実際に到達するには、その前のコードを最初に実行する必要があります。
そうです、UBが実行されると、以前の操作の影響は未定義になります。しかし、それが起こるまで、プログラムの実行は明確に定義されています。
ただし、これが発生するプログラムのすべての実行は、以前の操作を実行した後、その効果を元に戻すプログラムを含め、同等のプログラムに最適化できることに注意してください。その結果、先行するコードは、その効果が取り消されるのと同等になる場合はいつでも最適化される可能性があります。そうでなければ、それはできません。例については、以下を参照してください。
*注:これは、コンパイル時に発生するUBと矛盾しません。コンパイラがUBコードが常にすべての入力に対して実行されることを実際に証明できる場合、UBはコンパイル時間まで延長できます。ただし、これには、以前のすべてのコードが最終的にを返すことを知っている必要があります。これは強力な要件です。繰り返しますが、例/説明については以下を参照してください。
次のコードがあること、このコンクリート、メモしておく必要があります印刷foo
し、それを次のいずれかの未定義の動作に関係なく、あなたの入力を待ちます。
printf("foo")
getchar()
*(char*)1 = 1
ただし、foo
UBが発生した後も画面に表示される保証はないこと、または入力した文字が入力バッファーに存在しないことにも注意してください。これらの操作は両方とも「元に戻す」ことができ、UBの「タイムトラベル」と同様の効果があります。
場合はgetchar()
行がありませんでした、それがうラインが離れて最適化されるために法的なことだけならばならばそれは次のようになり区別できない出力からfoo
、その後、それを「非やって」。
この2つを区別できないかどうかは、実装(つまり、コンパイラと標準ライブラリ)に完全に依存します。たとえば、別のプログラムが出力を読み取るのを待っている間、ここでスレッドをprintf
ブロックできますか?それともすぐに戻りますか?
もちろん、コンパイラは特定のバージョンprintf
ので許容される動作を認識しているため、それに応じて最適化でき、その結果printf
、最適化される場合とされない場合があります。しかし、繰り返しになりますが、これはUBが以前の操作を行っていないことと区別がつかないということであり、以前のコードがUBのために「ポイズニング」されているということではありません。
a
(それ自体を計算することを除いて)使用されていないものを解決し、単に削除することができるかa