El comportamiento indefinido (Undefined Behavior, UB) es una situación en la que el estándar del lenguaje no define el comportamiento del programa. El compilador puede hacer cualquier cosa con el programa: el programa puede fallar, dar un resultado incorrecto o incluso 'funcionar'. El UB ocurre debido a errores como salir del límite de un arreglo, desreferenciar un puntero nulo, etc.
int arr[5]; arr[10] = 42; // UB: salida del límite del arreglo int* p = nullptr; *p = 1; // UB: desreferencia de 0
Para evitar el UB se puede seguir los estándares, utilizar herramientas modernas (ASan, UBSan, valgrind), tratar de no usar punteros crudos y escribir código seguro.
Si ocurrió UB en una parte del programa, ¿puede esto afectar a una parte del código completamente diferente e 'independiente'?
¡Sí! El compilador puede hacer transformaciones inesperadas durante la optimización si detecta que ocurrió un UB.
void foo(int* p) { if (p == nullptr) return; *p = 5; // ¡Y si p no era nullptr, esto es UB! Pero el compilador puede eliminar las verificaciones, asumiendo que p siempre es válido. }
Historia
En un servidor de una gran empresa, debido a la desreferencia de un puntero nulo, de vez en cuando ocurrían caídas aleatorias del proceso, que eran difíciles de reproducir: en debug todo funcionaba, pero en release no.
Historia
Al portar código de 32 bits a 64 bits se confundieron los tipos de datos, utilizando cast entre int y puntero. En algunas máquinas funcionaba, en otras aparecían caídas y artefactos extraños.
Historia
En Internet hay un caso conocido donde un inocente UB (salida de los límites del arreglo) rompía el funcionamiento de todo el programa: el compilador eliminó no solo el trabajo con el arreglo, sino que 'optimizó' parte del código que no estaba relacionada con el error.