Comportamiento indefinido en C y C++

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);
}
Producción

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  

  1. Puntos de secuencia en C | Serie 1
  2. «eliminar esto» en C++
  3. Pasar NULL a printf en C
  4. Accediendo a la array fuera de los límites en C/C++
  5. Uso de realloc()
  6. Ejecución de printf con operadores ++ Destrucción virtual usando shared_ptr en C++
  7. 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

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *