Undefiniertes Verhalten (Undefined Behavior, UB) ist eine Situation, in der der Standard der Sprache das Verhalten des Programms nicht definiert. Der Compiler kann mit dem Programm machen, was er will: Das Programm kann abstürzen, falsche Ergebnisse liefern oder sogar 'funktionieren'. UB tritt aufgrund von Fehlern auf, wie z.B. dem Überschreiten der Array-Grenzen, Dereferenzierung eines Nullzeigers usw.
int arr[5]; arr[10] = 42; // UB: Array-Grenzen überschreiten int* p = nullptr; *p = 1; // UB: Dereferenzierung von 0
UB vermeiden kann man durch die Einhaltung von Standards, die Verwendung moderner Werkzeuge (ASan, UBSan, valgrind), die Bemühung, rohe Zeiger zu vermeiden, und das Schreiben von sicherem Code.
Wenn UB in einem Teil des Programms auftritt, kann dies einen völlig anderen, 'unabhängigen' Teil des Codes beeinflussen?
Ja! Der Compiler kann während der Optimierung unerwartete Transformationen vornehmen, wenn festgestellt wird, dass UB aufgetreten ist.
void foo(int* p) { if (p == nullptr) return; *p = 5; // Und wenn p nicht nullptr war, ist das UB! Aber der Compiler könnte die Prüfungen entfernen, da er glaubt, dass p immer gültig ist. }
Geschichte
Auf dem Server eines großen Unternehmens traten aufgrund der Dereferenzierung eines Nullzeigers von Zeit zu Zeit zufällige Abstürze des Prozesses auf, die schwer zu reproduzieren waren: Im Debug-Modus funktionierte alles, im Release-Modus jedoch nicht.
Geschichte
Bei der Portierung von Code von 32-Bit auf 64-Bit wurden die Datentypen verwechselt und ein Cast zwischen int und Zeiger verwendet. Auf einigen Maschinen funktionierte es, auf anderen traten Abstürze und seltsame Artefakte auf.
Geschichte
Im Internet ist ein Fall bekannt, in dem harmloses UB (Überschreiten der Array-Grenzen) die gesamte Programmausführung störte: Der Compiler entfernte nicht nur die Arbeit mit dem Array, sondern 'optimierte' auch einen Teil des Codes, der nicht mit dem Fehler zusammenhing.