You are currently browsing the tag archive for the ‘variable’ tag.

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

En C++ existen varios modificadores de tipos de variables. El más conocido es const. Aquí queremos hablar de un modificador que de alguna forma es el contrario de const: El modificador mutable.

El modificador mutable quiere decir: Este miembro sigue siendo variable aunque la instancia de su clase sea declarada const. Fuera de una clase, mutable no tiene sentido.

La pregunta es: ¿por qué alguien quiere tener un miembro de clase variable cuando ha declarado el objeto const? Vemos un ejemplo clásico de la programación multi-hilo.

class Lista
{
public:
    // Una función que modifica la instancia
    void añadeElemento(const int nuevo_valor)
    {
        while (mutex)
        {
            // Bloquea el objeto para acceso exclusivo
            mutex = true;
            // Modifica el objeto
            una_lista.push_back(nuevo_valor);  
            // Libera el objeto
            mutex = false;
        }
    }
    
    // Una función que lee la instancia
    std::list<int> devuelveLista() const
    {
        std::list<int> copia;
        while (mutex)
        {
            // Bloquea el objeto para acceso exclusivo
            mutex = true;  
            // Crea una copia temporal
            copia = una_lista;
            // Libera el objeto
            mutex = false;
        }
        // Con la copia el hilo puede leer cuanto quiera
        return copia;  
    }
    
private:
    std::list<int> una_lista;
    mutable bool mutex;
};

La clase Lista contiene un objeto complejo una_lista y un mutex. En un entorno multi-hilo, todos los hilos podrían querer acceder a la lista potencialmente al mismo tiempo. No hay problema mientras todas los hilos quieren leer, pero esto se cambia si uno quiere escribir. Añadir un nuevo elemento requiere cambiar varias variables dentro de la lista. Con un poco de mala suerte (no mucha), un hilo intenta leer de la lista cuando el cambio todavía no está completo y obtiene así datos corruptos. Por esta razón, se bloquea la lista con un mutex. (Una tipo de mutex no muy sofisticado, pero para nuestro ejemplo nos sirve.) Mientras un hilo tiene el mutex (asignando true, los otros se deben esperar en el bucle while.

Si un hilo quiere leer la lista con la función devuelveLista, entonces, conceptualmente, lo puede hacer sobre un objeto constante. Leer no modifica el contenido del objeto y, en consequencia, la función devuelveLista está declarada const. No obstante, el miemro mutex también sería constante en este caso. Y ahora, ¿cómo se puede bloquear la lista contra el acceso de escritura en una función const?

La solución es declarar el mutex mutable. Así el mutex sigue siendo variable mientras el resto de la clase es constante.

Es obvio que sobrescribir el carácter constante de un objeto con mutable invita a crear un mal diseño. Por eso úsalo con consideración. Un miembro mutable suele contener meta-información como un guardian de acceso (como un mutex), o un contador que cuenta cuántas veces se ha leído un dato. Los datos mismos nunca tienen razón de ser mutable.

Lectura adicional

Escribe tu dirección de correo electrónico para suscribirte a este blog, y recibir notificaciones de nuevos mensajes por correo.

Únete a otros 52 seguidores

Archivos

diciembre 2018
L M X J V S D
« Nov    
 12
3456789
10111213141516
17181920212223
24252627282930
31