Entendiendo el calificador “volátil” en C | Conjunto 2 (Ejemplos)

 

La palabra clave volatile está destinada a evitar que el compilador aplique optimizaciones en objetos que pueden cambiar de formas que el compilador no puede determinar. 

Los objetos declarados como volátiles se omiten de la optimización porque sus valores pueden cambiarse mediante código fuera del alcance del código actual en cualquier momento. El sistema siempre lee el valor actual de un objeto volátil desde la ubicación de la memoria en lugar de mantener su valor en un registro temporal en el punto en que se solicita, incluso si una instrucción previa solicitó el valor del mismo objeto. Entonces, la pregunta simple es, ¿cómo puede cambiar el valor de una variable de tal manera que el compilador no pueda predecir? Considere los siguientes casos para obtener una respuesta a esta pregunta: 

1) Variables globales modificadas por una rutina de servicio de interrupción fuera del alcance: por ejemplo, una variable global puede representar un puerto de datos (generalmente un puntero global, denominado E/S mapeada en memoria) que se actualizará dinámicamente. El código que lee el puerto de datos debe declararse como volátil para obtener los últimos datos disponibles en el puerto. Si no se declara la variable como volátil, el compilador optimizará el código de tal manera que leerá el puerto solo una vez y seguirá usando el mismo valor en un registro temporal para acelerar el programa (optimización de velocidad). En general, se utiliza un ISR para actualizar estos puertos de datos cuando hay una interrupción debido a la disponibilidad de nuevos datos. 

 

2) Variables globales dentro de una aplicación de subprocesos múltiples: existen múltiples formas de comunicación de subprocesos, a saber, paso de mensajes, memoria compartida, buzones de correo, etc. Una variable global es una forma débil de memoria compartida. Cuando dos subprocesos comparten información a través de variables globales, esas variables deben calificarse como volátiles. Dado que los subprocesos se ejecutan de forma asíncrona, cualquier actualización de las variables globales debido a un subproceso debe ser recuperada recientemente por el otro subproceso del consumidor. El compilador puede leer las variables globales y colocarlas en variables temporales del contexto del subproceso actual. Para anular el efecto de las optimizaciones del compilador, dichas variables globales deben calificarse como volátiles.

Si no usamos el calificador volátil, pueden surgir los siguientes problemas: 
1) Es posible que el código no funcione como se esperaba cuando la optimización está activada. 
2) Es posible que el código no funcione como se espera cuando se habilitan y utilizan las interrupciones.

Veamos un ejemplo para entender cómo los compiladores interpretan la palabra clave volátil. Considere el siguiente código. Estamos cambiando el valor de un objeto const usando un puntero y estamos compilando código sin opción de optimización. Por lo tanto, el compilador no realizará ninguna optimización y cambiará el valor del objeto const.

C

/* Compile code without optimization option */
#include <stdio.h>
int main(void)
{
    const int local = 10;
    int *ptr = (int*) &local;
 
    printf("Initial value of local : %d \n", local);
 
    *ptr = 100;
 
    printf("Modified value of local: %d \n", local);
 
    return 0;
}

Cuando compilamos código con la opción “–save-temps” de gcc, genera 3 archivos de salida:
1) código preprocesado (con extensión .i) 
2) código ensamblador (con extensión .s) y 
3) código objeto (con extensión .o extensión). 

Compilamos el código sin optimización, es por eso que el tamaño del código ensamblador será mayor (que se resalta en rojo a continuación).

Producción: 

  [narendra@ubuntu]$ gcc volatile.c -o volatile –save-temps
  [narendra@ubuntu]$ ./volatile
  Initial value of local : 10
  Modified value of local: 100
  [narendra@ubuntu]$ ls -l volatile.s
  -rw-r–r– 1 narendra narendra 731 2016-11-19 16:19 volatile.s
  [narendra@ubuntu]$

Vamos a compilar el mismo código con la opción de optimización (es decir, la opción -O). En el siguiente código, «local» se declara como constante (y no volátil). El compilador GCC optimiza e ignora las instrucciones que intentan cambiar el valor del objeto const. Por lo tanto, el valor del objeto const sigue siendo el mismo. 

C

/* Compile code with optimization option */
#include <stdio.h>
 
int main(void)
{
    const int local = 10;
    int *ptr = (int*) &local;
 
    printf("Initial value of local : %d \n", local);
 
    *ptr = 100;
 
    printf("Modified value of local: %d \n", local);
 
    return 0;
}

Para el código anterior, el compilador realiza la optimización, por eso se reducirá el tamaño del código ensamblador.

Producción: 

  [narendra@ubuntu]$ gcc -O3 volatile.c -o volatile –save-temps
  [narendra@ubuntu]$ ./volatile
  Initial value of local : 10
  Modified value of local: 10
  [narendra@ubuntu]$ ls -l volatile.s
  -rw-r–r– 1 narendra narendra 626 2016-11-19 16:21 volatile.s

Declaremos el objeto const como volátil y compilemos el código con la opción de optimización. Aunque compilamos el código con la opción de optimización, el valor del objeto const cambiará porque la variable se declara como volátil, lo que significa que no realice ninguna optimización. 

C

/* Compile code with optimization option */
#include <stdio.h>
 
int main(void)
{
    const volatile int local = 10;
    int *ptr = (int*) &local;
 
    printf("Initial value of local : %d \n", local);
 
    *ptr = 100;
 
    printf("Modified value of local: %d \n", local);
 
    return 0;
}

Producción: 

  [narendra@ubuntu]$ gcc -O3 volatile.c -o volatile –save-temp
  [narendra@ubuntu]$ ./volatile
  Initial value of local : 10
  Modified value of local: 100
  [narendra@ubuntu]$ ls -l volatile.s
  -rw-r–r– 1 narendra narendra 711 2016-11-19 16:22 volatile.s
  [narendra@ubuntu]$

El ejemplo anterior puede no ser un buen ejemplo práctico, pero el propósito era explicar cómo los compiladores interpretan la palabra clave volátil. Como ejemplo práctico, piense en el sensor táctil de los teléfonos móviles. El controlador que extrae el sensor táctil leerá la ubicación del toque y la enviará a aplicaciones de nivel superior. El controlador en sí no debe modificar (const-ness) la ubicación de lectura y asegurarse de que lee la entrada táctil cada vez que se actualiza (volatilidad). Dicho controlador debe leer la entrada del sensor táctil de manera constante y volátil.

Nota: Los códigos anteriores son específicos del compilador y es posible que no funcionen en todos los compiladores. El propósito de los ejemplos es hacer que los lectores entiendan el concepto.

Artículo relacionado: 
Entendiendo el calificador “volátil” en C | Conjunto 1 (Introducción)
Consulte los siguientes enlaces para obtener más detalles sobre la palabra clave volátil: 
Volátil: el mejor amigo de un programador  
No use volátil como una primitiva de sincronización
Este artículo fue compilado por «Narendra Kangralkar» y revisado por el equipo de GeeksforGeeks. Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.
 

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 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 *