Cuando ejecutamos un código, a veces vemos resultados absurdos en lugar del resultado esperado. Por lo tanto, en la programación C/C++, el comportamiento indefinido significa que el programa no se puede compilar o puede ejecutarse incorrectamente, falla o genera resultados incorrectos, o cuando fortuitamente puede hacer exactamente lo que el programador pretendía. Siempre que el resultado de un programa en ejecución es impredecible, se dice que tiene un comportamiento indefinido.
Como programador de C, comprender el comportamiento indefinido es muy importante para una codificación óptima y para que el programa produzca una buena eficiencia, especialmente cuando se trata de códigos C integrados en el diseño del sistema.
Ejemplos:
División por cero
int val = 5; return val / 0; // undefined behavior
Accesos a la memoria fuera de los límites de la array
int arr[4] = {0, 1, 2, 3}; return arr[5]; // undefined behavior for indexing out of bounds
Desbordamiento de enteros con signo
int x = INT_MAX; printf("%d", x + 1); // undefined behavior
Desreferencia de puntero nulo
val = 0; int ptr = *val; // undefined behavior for dereferencing a null pointer
Modificación de string literal
char* s = "geeksforgeeks"; s[0] = 'e'; // undefined behavior
Acceso a un puntero NULL, etc.
int* ptr = NULL; printf("%d", *ptr); // undefined behavior for accessing NULL Pointer
A veces, los compiladores pueden diagnosticar errores simples; sin embargo, a veces no están diseñados para diagnosticar el comportamiento indefinido.
Los siguientes son algunos programas C/C++ que muestran un comportamiento indefinido:
Programa 1:
C++
// C++ Program to demonstrate // division by 0 #include <iostream> using namespace std; int main() { int x = 25, y = 0; int z = x / y; cout << z; return 0; } // This code is contributed by sarajadhav12052009
C
// C Program to demonstrate // division by 0 #include <stdio.h> // Driver Code int main() { int x = 25, y = 0; int z = x / y; printf("%d", z); return 0; }
Programa 2:
C++
// C++ Program to demonstrate // Uninitialized variables #include <iostream> using namespace std; int main() { bool val; if (val) printf("TRUE"); else printf("FALSE"); } // This is contributed by sarajadhav12052009
C
// C Program to demonstrate // Uninitialized variables #include <stdio.h> // Driver Code int main(void) { typedef enum {False, True} bool; bool val; if (val) printf("TRUE"); else printf("FALSE"); }
Programa 3:
C++
// C++ Program to demonstrate // accessing value of NULL pointer #include <iostream> using namespace std; int main() { int* ptr = NULL; cout << *ptr; return 0; } // This code is contributed by sarajadhav12052009
C
// C Program to demonstrate // accessing value of NULL // pointer #include <stdio.h> // Driver Code int main() { int* ptr = NULL; printf("%d", *ptr); return 0; }
Programa 4:
C++
// C++ program to demonstrate // accessing out of bound #include <iostream> using namespace std; int main() { int arr[5]; // We access arr[5] in last iteration. for (int i = 0; i <= 5; i++) cout << arr[i]; return 0; } // This code is contributed by sarajadhav12052009
C
// C program to demonstrate // accessing out of bound #include <stdio.h> // Driver Code int main() { int arr[5]; // We access arr[5] in last iteration. for (int i = 0; i <= 5; i++) printf("%d ", arr[i]); return 0; //edited by malav.shubham17 }
Programa 5:
C++
// C++ Program to demonstrate going // beyond limit of signed int #include <iostream> #include <climits> using namespace std; int main() { int x = INT_MAX; cout << x + 1;; return 0; } // This code is contributed by sarajadhav12052009
C
// C Program to demonstrate going // beyond limit of signed int #include <stdio.h> #include <limits.h> // Driver Code int main() { int x = INT_MAX; printf("%d", x + 1); return 0; }
Programa 6:
C++
// C++ Program to demonstrate trying to // modify a string literal #include <iostream> using namespace std; int main() { char* s = "geeksforgeeks"; s[0] = 'e'; return 0; } // This code is contributed by sarajadhav12052009
C
// C Program to demonstrate trying to // modify a string literal #include <stdio.h> // Driver Code int main() { char* s = "geeksforgeeks"; s[0] = 'e'; return 0; }
Programa 7:
C++
// C++ Program to demonstrate modifying a variable // multiple times before a defined sequence point #include <iostream> using namespace std; int main() { int i = 8; int p = i++ * i++; cout << p; } // This code is contributed by sarajadhav12052009
C
// C Program to demonstrate modifying a variable // multiple times before a defined // sequence point #include <stdio.h> // Driver Code int main() { int i = 8; int p = i++ * i++; printf("%d\n", p); }
72
Explicación: el programa produce 72 como salida en la mayoría de los compiladores, pero implementar software basado en esta suposición no es una buena idea.
La salida de todos los programas anteriores es impredecible (o indefinida ). Los compiladores (que implementan el estándar C/C++) son libres de hacer cualquier cosa, ya que no están definidos por los estándares C y C++.
Lenguajes como Java, atrapan errores tan pronto como se encuentran, pero lenguajes como C y C++ en algunos casos continúan ejecutando el código de manera defectuosa, lo que puede generar resultados impredecibles. El programa puede fallar con cualquier tipo de mensaje de error, o puede corromper los datos sin saberlo, lo cual es un problema grave que tratar.
Importancia de conocer el comportamiento indefinido: si un usuario comienza a aprender en un entorno C/C++ y no tiene claro el concepto de comportamiento indefinido, eso puede traer muchos problemas en el futuro mientras que la depuración del código de otra persona puede ser realmente difícil para rastrear la raíz. al error indefinido.
Riesgos y desventajas del comportamiento indefinido
- Los programadores a veces confían en una implementación particular (o compilador) de comportamiento indefinido que puede causar problemas cuando se cambia/actualiza el compilador. Por ejemplo, el último programa produce 72 como salida en la mayoría de los compiladores, pero implementar software basado en esta suposición no es una buena idea.
- Los comportamientos indefinidos también pueden causar vulnerabilidades de seguridad, especialmente debido a los casos en que no se verifica una array fuera de límite (provoca un ataque de desbordamiento de búfer).
Ventajas del comportamiento indefinido
- C y C++ tienen comportamientos indefinidos porque permite a los compiladores evitar muchas verificaciones. Supongamos que un conjunto de código con una array de mayor rendimiento no necesita observar los límites, lo que evita la necesidad de un paso de optimización complejo para verificar tales condiciones fuera de los bucles. Los bucles estrechamente vinculados aceleran el programa de un treinta a un cincuenta por ciento cuando obtiene la ventaja de la naturaleza indefinida del desbordamiento firmado, que generalmente ofrece el compilador de C.
- También tenemos otra ventaja de esto, ya que nos permite almacenar el valor de una variable en un registro del procesador y manipularlo a lo largo del tiempo que es más grande que la variable en el código fuente.
- También ayuda en las comprobaciones de tiempo de compilación que no serían posibles sin un mayor conocimiento del comportamiento indefinido en el compilador de C/C++.
Más ejemplos de comportamiento indefinido
- Puntos de secuencia en C | Serie 1
- «eliminar esto» en C++
- Pasar NULL a printf en C
- Accediendo a la array fuera de los límites en C/C++
- Uso de realloc()
- Ejecución de printf con operadores ++ Destrucción virtual usando shared_ptr en C++
- Destructor virtual
Publicación traducida automáticamente
Artículo escrito por Chinmoy Lenka y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA