ProgramaciónProgramador de sistemas C++

¿Qué es el comportamiento indefinido (undefined behavior) en C++? Proporcione ejemplos y explique cómo evitarlo.

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

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.

Ejemplo de UB explícito:

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.

Pregunta con trampa.

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.

Ejemplo:

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. }

Ejemplos de errores reales debido al desconocimiento de los detalles del tema.


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.