Die beste Antwort ist ein falsches (aber häufiges) Missverständnis:
Undefiniertes Verhalten ist eine Laufzeit- Eigenschaft *. Es kann nicht "Zeitreise"!
Bestimmte Operationen sind (standardmäßig) so definiert, dass sie Nebenwirkungen haben und nicht wegoptimiert werden können. Vorgänge, die E / A ausführen oder auf volatile
Variablen zugreifen , fallen in diese Kategorie.
Es gibt jedoch eine Einschränkung: UB kann ein beliebiges Verhalten sein, einschließlich eines Verhaltens, das frühere Operationen rückgängig macht . Dies kann in einigen Fällen ähnliche Konsequenzen haben wie die Optimierung früherer Codes.
Tatsächlich stimmt dies mit dem Zitat in der oberen Antwort überein (Hervorhebung von mir):
Eine konforme Implementierung, die ein wohlgeformtes Programm ausführt, muss dasselbe beobachtbare Verhalten erzeugen wie eine der möglichen Ausführungen der entsprechenden Instanz der abstrakten Maschine mit demselben Programm und derselben Eingabe.
Wenn eine solche Ausführung jedoch eine undefinierte Operation enthält, stellt diese Internationale Norm keine Anforderung an die Implementierung , die dieses Programm mit dieser Eingabe ausführt (nicht einmal in Bezug auf Operationen, die der ersten undefinierten Operation vorausgehen).
Ja, dieses Zitat tut sagen , „nicht einmal im Hinblick auf den Operationen den ersten undefinierten Betrieb vorangestellten“ , aber feststellen , dass dies speziell über Code, wird ausgeführt , nicht nur zusammengestellt.
Schließlich bewirkt undefiniertes Verhalten, das nicht tatsächlich erreicht wird, nichts, und damit die Zeile mit UB tatsächlich erreicht wird, muss der vorhergehende Code zuerst ausgeführt werden!
Ja, sobald UB ausgeführt wird , werden alle Auswirkungen früherer Operationen undefiniert. Bis dahin ist die Ausführung des Programms jedoch genau definiert.
Beachten Sie jedoch, dass alle Ausführungen des Programms, die zu diesem Ereignis führen, auf äquivalente Programme optimiert werden können, einschließlich aller Programme, die vorherige Vorgänge ausführen, deren Auswirkungen dann jedoch nicht mehr ausgeführt werden. Folglich kann der vorhergehende Code immer dann optimiert werden, wenn dies gleichbedeutend damit ist, dass ihre Auswirkungen rückgängig gemacht werden . sonst kann es nicht. Ein Beispiel finden Sie weiter unten.
* Hinweis: Dies ist nicht unvereinbar mit UB, das zur Kompilierungszeit auftritt . Wenn der Compiler tatsächlich nachweisen kann , dass Code UB wird immer für alle Eingänge ausgeführt wird, dann kann UB zu Kompilierung verlängern. Dies setzt jedoch voraus, dass der gesamte vorherige Code schließlich zurückgegeben wird , was eine wichtige Voraussetzung ist. Ein Beispiel / eine Erklärung finden Sie weiter unten.
Beachten Sie dazu, dass der folgende Code gedruckt und auf Ihre Eingabe gewartet werden muss,foo
unabhängig von einem darauf folgenden undefinierten Verhalten:
printf("foo");
getchar();
*(char*)1 = 1;
Beachten Sie jedoch auch, dass es keine Garantie gibt, foo
die nach dem Auftreten der UB auf dem Bildschirm angezeigt wird, oder dass sich das von Ihnen eingegebene Zeichen nicht mehr im Eingabepuffer befindet. Beide Vorgänge können "rückgängig gemacht" werden, was einen ähnlichen Effekt wie "Zeitreise" von UB hat.
Wenn die getchar()
Leitung nicht vorhanden wäre, wäre es legal, die Leitungen genau dann zu optimieren, wenn dies nicht von der Ausgabe zu unterscheiden wäre foo
und sie dann "nicht mehr zu tun" wäre.
Ob die beiden nicht zu unterscheiden sind oder nicht, hängt vollständig von der Implementierung ab (dh von Ihrem Compiler und Ihrer Standardbibliothek). Können Sie beispielsweise Ihren Thread hier printf
blockieren , während Sie darauf warten, dass ein anderes Programm die Ausgabe liest? Oder wird es sofort zurückkehren?
Wenn es hier blockieren kann, kann ein anderes Programm das Lesen seiner vollständigen Ausgabe verweigern, und es kann niemals zurückkehren, und folglich kann UB niemals tatsächlich auftreten.
Wenn es hier sofort zurückkehren kann, dann wissen wir, dass es zurückkehren muss, und daher ist eine Optimierung nicht zu unterscheiden, wenn es ausgeführt und dann seine Auswirkungen aufgehoben werden.
Da der Compiler weiß, welches Verhalten für seine bestimmte Version von zulässig ist printf
, kann er natürlich entsprechend optimieren und printf
kann daher in einigen Fällen und nicht in anderen Fällen optimiert werden. Die Rechtfertigung ist jedoch wiederum, dass dies nicht von der UB zu unterscheiden ist, die frühere Operationen nicht ausführt, und nicht, dass der vorherige Code aufgrund von UB "vergiftet" ist.
a
a