Die meisten Arten von UB, über die wir uns normalerweise Sorgen machen, wie NULL-Deref oder Division durch Null, sind Laufzeit- UB. Das Kompilieren einer Funktion, die bei Ausführung zur Laufzeit UB führen würde, darf nicht zum Absturz des Compilers führen. Es sei denn, es kann vielleicht beweisen, dass die Funktion (und dieser Pfad durch die Funktion) definitiv wird durch das Programm ausgeführt werden.
(2. Gedanke: Vielleicht habe ich beim Kompilieren nicht berücksichtigt, dass Template / Constexpr eine Evaluierung erforderlich macht. Möglicherweise darf UB während der Übersetzung willkürliche Verrücktheiten verursachen, selbst wenn die resultierende Funktion niemals aufgerufen wird.)
Das Verhalten während der Übersetzung des ISO C ++ - Zitats in der Antwort von @ StoryTeller ähnelt der im ISO C-Standard verwendeten Sprache. C enthält keine Vorlagen oder constexpr
obligatorische Auswertungen zur Kompilierungszeit.
Aber lustige Tatsache : ISO C sagt in einem Hinweis, dass die Beendigung einer Übersetzung mit einer Diagnosemeldung erfolgen muss. Oder "Verhalten während der Übersetzung ... auf dokumentierte Weise". Ich denke nicht, dass "die Situation vollständig ignorieren" so verstanden werden könnte, dass die Übersetzung gestoppt wird.
Alte Antwort, geschrieben, bevor ich etwas über die Übersetzungszeit von UB erfuhr. Dies gilt jedoch für Runtime-UB und ist daher möglicherweise immer noch nützlich.
Es gibt keine UB, die zur Kompilierungszeit passiert . Es kann für den Compiler entlang eines bestimmten Ausführungspfads sichtbar sein , aber in C ++ ist dies nicht geschehen erst wenn die Ausführung diesen Ausführungspfad über eine Funktion erreicht hat.
Fehler in einem Programm, die das Kompilieren unmöglich machen, sind nicht UB, sondern Syntaxfehler. Ein solches Programm ist in der C ++ - Terminologie "nicht wohlgeformt" (wenn ich meine Standardsprache korrekt habe). Ein Programm kann wohlgeformt sein, aber UB enthalten. Unterschied zwischen undefiniertem Verhalten und schlecht geformt, keine Diagnosemeldung erforderlich
Wenn ich nichts falsch verstehe, muss dieses Programm in ISO C ++ korrekt kompiliert und ausgeführt werden, da die Ausführung niemals die Division durch Null erreicht. (In der Praxis ( Godbolt ) machen gute Compiler nur funktionierende ausführbare Dateien. Gcc / clang warnt davor, x / 0
aber nicht davor , selbst wenn sie optimiert werden. Trotzdem versuchen wir zu sagen, wie niedrig ISO C ++ die Qualität der Implementierung sein lässt. Überprüfen Sie also gcc / clang ist kaum ein nützlicher Test, außer um zu bestätigen, dass ich das Programm richtig geschrieben habe.)
int cause_UB() {
int x=0;
return 1 / x; // UB if ever reached.
// Note I'm avoiding x/0 in case that counts as translation time UB.
// UB still obvious when optimizing across statements, though.
}
int main(){
if (0)
cause_UB();
}
Ein Anwendungsfall hierfür könnte der C-Präprozessor oder constexpr
Variablen und die Verzweigung dieser Variablen sein, was zu Unsinn in einigen Pfaden führt, die für diese Auswahl von Konstanten nie erreicht werden.
Es kann davon ausgegangen werden, dass Ausführungspfade, die eine zur Kompilierungszeit sichtbare UB verursachen, niemals verwendet werden, z. B. könnte ein Compiler für x86 eine ud2
(Ursache für eine Anweisung für unzulässige Anweisungen) als Definition für ausgeben cause_UB()
. Oder innerhalb einer Funktion kann der Zweig entfernt werden , wenn eine Seite von if()
zu nachweisbarem UB führt .
Aber der Compiler muss immer noch alles andere auf vernünftige und korrekte Weise kompilieren . Alle Pfade, die UB nicht begegnen (oder deren Nachweis nicht nachgewiesen werden kann), müssen weiterhin zu asm kompiliert werden, das so ausgeführt wird, als ob die abstrakte C ++ - Maschine es ausführen würde.
Sie könnten argumentieren, dass die bedingungslose UB in der Kompilierungszeit main
eine Ausnahme von dieser Regel darstellt. Oder auf andere Weise zur Kompilierungszeit nachweisbar, dass die Ausführung ab beginntmain
tatsächlich die garantierte UB erreicht.
Ich würde immer noch argumentieren, dass das Verhalten von legalen Compilern das Produzieren einer Granate beinhaltet, die explodiert, wenn sie ausgeführt wird. Oder plausibler, eine Definition main
davon besteht aus einer einzigen illegalen Anweisung. Ich würde behaupten, wenn Sie das Programm nie ausführen, gibt es noch keine UB. Der Compiler selbst darf nicht explodieren, IMO.
Funktionen, die mögliche oder nachweisbare UB innerhalb von Zweigen enthalten
UB auf einem bestimmten Ausführungspfad reicht rechtzeitig zurück, um den gesamten vorherigen Code zu "kontaminieren". In der Praxis können Compiler diese Regel jedoch nur dann nutzen, wenn sie tatsächlich nachweisen können , dass Ausführungspfade zu UB führen, die zur Kompilierungszeit sichtbar sind. z.B
int minefield(int x) {
if (x == 3) {
*(char*)nullptr = x/0;
}
return x * 5;
}
Der Compiler muss asm erstellen, das für alle x
anderen als 3 funktioniert , bis zu den Punkten, an denen x * 5
bei INT_MIN und INT_MAX ein UB mit Vorzeichenüberlauf verursacht wird. Wenn diese Funktion nie mit aufgerufen wird x==3
, enthält das Programm natürlich keine UB und muss wie geschrieben funktionieren.
Wir hätten genauso gut if(x == 3) __builtin_unreachable();
in GNU C schreiben können , um dem Compiler mitzuteilen, dass dies x
definitiv nicht 3 ist.
In der Praxis gibt es in normalen Programmen überall "Minenfeld" -Code. Beispielsweise verspricht jede Division durch eine Ganzzahl dem Compiler, dass sie nicht Null ist. Jeder Zeiger-Deref verspricht dem Compiler, dass er nicht NULL ist.