Ongedefinieerd gedrag (Undefined Behavior, UB) is een situatie waarin de standaard van de taal het gedrag van het programma niet definieert. De compiler kan met het programma doen wat hij maar wil: het programma kan crashen, onjuiste resultaten geven of zelfs 'werken'. UB ontstaat door fouten zoals het overschrijden van de array grenzen, dereferencen van een null pointer, enz.
int arr[5]; arr[10] = 42; // UB: overschrijding van array grenzen int* p = nullptr; *p = 1; // UB: dereferencen van 0
UB te vermijden is mogelijk door de standaarden na te leven, moderne tools te gebruiken (ASan, UBSan, valgrind), te proberen rauwe pointers niet te gebruiken en veilige code te schrijven.
Als UB in één deel van het programma optreedt, kan dit dan invloed hebben op een geheel ander, 'onafhankelijk' deel van de code?
Ja! De compiler kan tijdens optimalisatie onverwachte transformaties doen als hij ontdekt dat er UB is opgetreden.
void foo(int* p) { if (p == nullptr) return; *p = 5; // Maar als p niet nullptr was, is dit UB! De compiler kan de controles verwijderen, aangezien hij denkt dat p altijd geldig is. }
Verhaal
Op de server van een groot bedrijf traden af en toe willekeurige crashes van het proces op door dereferencen van een null pointer, die moeilijk te reproduceren waren: in debug werkte alles, maar in release niet.
Verhaal
Bij het porteren van code van 32-bit naar 64-bit werden de datatypes door elkaar gehaald, en werd er gecast tussen int en pointer. Op de ene machines werkte het, terwijl er op andere machines crashes en vreemde artefacten verschenen.
Verhaal
Er is een geval op internet bekend waarbij onschuldige UB (overschrijding van de array) de werking van het hele programma verstoorde: de compiler verwijderde niet alleen de omgang met de array, maar 'optimaliseerde' ook een deel van de code dat niets met de fout te maken had.