En C++ existen varios modificadores de tipos de variables. El más conocido es const. Aquí queremos hablar de un modificador más oscuro que const: volatile.

El modificador volatile quiere decir: Esta variable no sólo modifica tu programa sino alguien más.

La pregunta es: ¿quién más cambia las variables de tu programa? Pues, un componente de hardware, por ejemplo. Cuando escribes un controlador de hardware, entonces usas volatile bastante a menudo.

Los dispositivos de hardware se “mapean” (asignan) a ciertas direcciones de memoria. Por ejemplo, en la dirección 100 no escribes en RAM, pero en un registro de la tarjeta gráfica. Si escribes ahí, digamos, un cinco, entonces la tarjeta gráfica lo entiende como una orden de poner la pantalla en negro. El hardware real es ligermanete más sofisticado, pero esto es el principio: ciertas direcciones de memoria corresponden a registros de dispositivos de hardware.

Lógicamente debes asegurar que tu programa realmente escribe en esta dirección. Y que también realmente lee desde ahí. Porque si una variable corresponde a una dirección donde la tarjeta gráfica devuele un valor de error, entonces no te puedes fiar del último valor que esta variable tenía en tu programa. Debes realmente leerlo desde ahí. Y esto es la tarea del modificador volatile. Dice al compilador que, por favor, no optimice el uso de esta variable.

Y ahora: ¿Por qué tu programa no siempre lee y escribe en la posición de memoria a que la variable corresponde?

Esto tiene que ver con la optimización del código. Considera el siguiente trozo de código.

const int a = 5;
int b = a + 3;
b = 4;

Sin optimización, la constante a corresponde, digamos, a la dirección 8. La constante b a la dirección 12.

  1. Si el compilador no optimiza, entonces traduce la primera línea en un comando de escribir un 5 en la dirección 8.
  2. La segunda línea traduce en leer lo que pone en la dirección 8, sumarle 3 y escribirlo a la dirección 12.
  3. Luego escribe un 4 en la dirección 12.

No obstante, esto se puede optimizar para ganar velocidad. La constante a corresponde a un 5. Así podría modificar la segunda línea en.

int b = 5 + 3;

o directamente en

int b = 8;

Como se escribe el valor 4 en la variable b directamente después, entonces el compilador podría borrar las dos primeras líneas. El resultado es el mismo.

A menos si las variables a y b corresponden a direcciones de dispositivos de hardware. Para ellos, sí, importa que se lee desde la dirección que corresponde a la constante a y se escribe en la dirección de b. El mismo programa con volatile puede tener un significativo bastante distinto.

const volatile int a = 5;
volatile int b = a + 3;
b = 4;
  1. Declara un campo de lectura del despositivo y asígnale el valor por de defecto 5.
  2. Lee el nuevo valor de a, súmale 3 y escríbelo en la dirección de b.
  3. Escribe un nuevo valor en la dirección de b.

Bajo este punto de vista tiene también sentido de escribir dos valores distintos en b sin utilizar el valor en el programa. Lo puedes entender como un comando que ejecuta, por ejemplo, la tarjeta gráfica y no tu programa.

Pero no olvides, todo esto sólo tiene efecto si el compilador optimiza el programa como lo suele hacer para una versión de release.

Por cierto, si tienes la paranoia que cualquier variable se podría cambiar desde fuera, entonces no necesitas declarar todas volatile. Basta compilar tu programa sin optimizaciones – es decir, en modo de depuración.

Lectura adicional