variable software significa que qualifier language c declaration volatile

software - ¿Por qué se necesita la volatilidad en C?



volatile c++ (17)

En el lenguaje diseñado por Dennis Ritchie, todo acceso a cualquier objeto, excepto los objetos automáticos cuya dirección no se había tomado, se comportaría como si computara la dirección del objeto y luego leyera o escribía el almacenamiento en esa dirección. Esto hizo que el lenguaje fuera muy poderoso, pero con oportunidades de optimización muy limitadas.

Si bien podría haber sido posible agregar un calificador que invitaría a un compilador a asumir que un objeto en particular no se cambiaría de formas extrañas, tal suposición sería apropiada para la gran mayoría de los objetos en programas C, y habría sido no fue práctico agregar un calificador a todos los objetos para los cuales sería apropiado tal suposición. Por otro lado, algunos programas necesitan usar algunos objetos para los cuales tal suposición no sería válida. Para resolver este problema, el Estándar dice que los compiladores pueden asumir que los objetos que no se declaran volatile no tendrán su valor observado o cambiado en formas que estén fuera del control del compilador, o que queden fuera del alcance de un entendimiento razonable del compilador.

Debido a que varias plataformas pueden tener diferentes formas en que los objetos podrían observarse o modificarse fuera del control de un compilador, es apropiado que los compiladores de calidad para esas plataformas deban diferir en su manejo exacto de la semántica volatile . Desafortunadamente, debido a que el Estándar no sugirió que los compiladores de calidad destinados a la programación de bajo nivel en una plataforma deberían manejar la volatile de manera que reconozca todos los efectos relevantes de una operación de lectura / escritura particular en esa plataforma, muchos compiladores no alcanzan hacerlo de manera que sea más difícil procesar cosas como la E / S de fondo de una manera que sea eficiente pero que no pueda ser interrumpida por las "optimizaciones" del compilador.

¿Por qué se necesita la volatile en C? ¿Para qué se usa esto? ¿Qué hará?


En mi opinión, no deberías esperar demasiado de lo volatile . Para ilustrar, mira el ejemplo en la respuesta altamente votada de Nils Pipenbrinck .

Yo diría que su ejemplo no es adecuado para volatile . volatile solo se utiliza para: evitar que el compilador realice optimizaciones útiles y deseables . No tiene nada que ver con la seguridad del hilo, el acceso atómico o incluso el orden de la memoria.

En ese ejemplo:

void SendCommand (volatile MyHardwareGadget * gadget, int command, int data) { // wait while the gadget is busy: while (gadget->isbusy) { // do nothing here. } // set data first: gadget->data = data; // writing the command starts the action: gadget->command = command; }

el gadget->data = data anterior a gadget->command = command solo está garantizado en el código compilado por compilador. En el tiempo de ejecución, el procesador todavía posiblemente reordena la asignación de datos y comandos, con respecto a la arquitectura del procesador. El hardware podría obtener los datos incorrectos (suponga que el gadget se asigna a la E / S del hardware). La barrera de la memoria es necesaria entre los datos y la asignación de comandos.


En términos simples, le dice al compilador que no haga ninguna optimización en una variable en particular. Las variables que se asignan al registro del dispositivo son modificadas indirectamente por el dispositivo. En este caso, se debe utilizar volátil.


Hay dos usos. Estos se utilizan especialmente más a menudo en el desarrollo integrado.

  1. El compilador no optimizará las funciones que utilizan variables definidas con palabras clave volátiles

  2. Volatile se usa para acceder a ubicaciones de memoria exactas en RAM, ROM, etc. Esto se usa más a menudo para controlar dispositivos asignados en memoria, acceder a registros de CPU y localizar ubicaciones de memoria específicas.

Ver ejemplos con listado de montaje. Re: Uso de la palabra clave C "volatile" en el desarrollo integrado


La volatilidad también es útil cuando se quiere forzar al compilador a no optimizar una secuencia de código específica (por ejemplo, para escribir un micro-punto de referencia).



Mencionaré otro escenario donde los volátiles son importantes.

Supongamos que asigna un archivo a la memoria para una E / S más rápida y que el archivo puede cambiar entre bambalinas (por ejemplo, el archivo no se encuentra en su disco duro local, sino que se envía a través de la red a otra computadora).

Si accede a los datos del archivo asignado en memoria a través de punteros a objetos no volátiles (en el nivel del código fuente), el código generado por el compilador puede obtener los mismos datos varias veces sin que usted lo sepa.

Si los datos cambian, su programa puede usar dos o más versiones diferentes de los datos y entrar en un estado inconsistente. Esto puede llevar no solo a un comportamiento lógicamente incorrecto del programa sino también a agujeros de seguridad explotables en él si procesa archivos no confiables o archivos de ubicaciones no confiables.

Si se preocupa por la seguridad, y debería, este es un escenario importante a considerar.


Mi explicación simple es:

En algunos escenarios, según la lógica o el código, el compilador realizará la optimización de las variables que cree que no cambian. La palabra clave volatile impide que una variable sea optimizada.

Por ejemplo:

bool usb_interface_flag = 0; while(usb_interface_flag == 0) { // execute logic for the scenario where the USB isn''t connected }

A partir del código anterior, el compilador puede pensar que usb_interface_flag se define como 0, y que en el bucle while será cero para siempre. Después de la optimización, el compilador lo tratará como while(true) todo el tiempo, dando como resultado un bucle infinito.

Para evitar este tipo de escenarios, declaramos la bandera como volátil, le estamos diciendo al compilador que este valor puede ser cambiado por una interfaz externa u otro módulo del programa, es decir, no lo optimice. Ese es el caso de uso de volatile.


Otro uso para volatile es el manejo de señales. Si tienes código como este:

quit = 0; while (!quit) { /* very small loop which is completely visible to the compiler */ }

Al compilador se le permite notar que el cuerpo del bucle no toca la variable quit y convierte el bucle en un bucle while (true) . Incluso si la variable quit se establece en el controlador de señal para SIGINT y SIGTERM ; El compilador no tiene manera de saber eso.

Sin embargo, si la variable quit se declara volatile , el compilador se ve obligado a cargarla cada vez, porque se puede modificar en otro lugar. Esto es exactamente lo que quieres en esta situación.


Se puede cambiar un elemento volátil desde fuera del código compilado (por ejemplo, un programa puede asignar una variable volátil a un registro asignado en memoria). El compilador no aplicará ciertas optimizaciones al código que maneja una variable volátil; t cargarlo en un registro sin escribirlo en la memoria. Esto es importante cuando se trata de registros de hardware.


Un uso marginal para volátiles es el siguiente. Digamos que quieres calcular la derivada numérica de una función f :

double der_f(double x) { static const double h = 1e-3; return (f(x + h) - f(x)) / h; }

El problema es que x+hx generalmente no es igual a h debido a errores de redondeo. Piénselo: cuando reste números muy cercanos, perderá muchos dígitos significativos que pueden arruinar el cálculo de la derivada (piense en 1.00001 - 1). Una posible solución podría ser

double der_f2(double x) { static const double h = 1e-3; double hh = x + h - x; return (f(x + hh) - f(x)) / hh; }

pero dependiendo de la plataforma y los conmutadores del compilador, la segunda línea de esa función puede ser eliminada por un compilador que optimice agresivamente. Así que escribes en su lugar

volatile double hh = x + h; hh -= x;

para obligar al compilador a leer la ubicación de la memoria que contiene hh, perder una oportunidad de optimización final.


Vea este artículo de Andrei Alexandrescu, " volatile - El mejor amigo del programador multiproceso "

La palabra clave volátil se diseñó para evitar las optimizaciones del compilador que podrían hacer que el código sea incorrecto en presencia de ciertos eventos asíncronos. Por ejemplo, si declara una variable primitiva como volátil , al compilador no se le permite almacenar en caché en un registro, una optimización común que sería desastrosa si esa variable se compartiera entre varios subprocesos. Entonces, la regla general es que si tiene variables de tipo primitivo que deben compartirse entre varios subprocesos, declare esas variables volátiles . Pero puede hacer mucho más con esta palabra clave: puede usarla para capturar código que no es seguro para subprocesos, y puede hacerlo en tiempo de compilación. Este artículo muestra cómo se hace; La solución involucra un simple puntero inteligente que también facilita la serialización de secciones críticas de código.

El artículo se aplica tanto a C como a C++ .

También vea el artículo " C ++ y los peligros del bloqueo de doble control " por Scott Meyers y Andrei Alexandrescu:

Por lo tanto, cuando se trata de algunas ubicaciones de memoria (por ejemplo, puertos asignados en memoria o memoria referenciada por ISR [Rutinas de servicio de interrupción]), se deben suspender algunas optimizaciones. volatile existe para especificar un tratamiento especial para dichas ubicaciones, específicamente: (1) el contenido de una variable volátil es "inestable" (puede cambiar por medios desconocidos para el compilador), (2) todas las escrituras en datos volátiles son "observables" por lo que debe ejecutarse religiosamente, y (3) todas las operaciones en datos volátiles se ejecutan en la secuencia en la que aparecen en el código fuente. Las dos primeras reglas aseguran la correcta lectura y escritura. El último permite la implementación de protocolos de E / S que combinan entrada y salida. Esto es informalmente lo que las garantías volátiles de C y C ++.


Volatile le dice al compilador que no optimice nada que tenga que ver con la variable volátil.

Solo hay una razón para usarlo: cuando se conecta con el hardware.

Digamos que tiene una pequeña pieza de hardware que se asigna a RAM en algún lugar y que tiene dos direcciones: un puerto de comando y un puerto de datos:

typedef struct { int command; int data; int isbusy; } MyHardwareGadget;

Ahora quieres enviar algún comando:

void SendCommand (MyHardwareGadget * gadget, int command, int data) { // wait while the gadget is busy: while (gadget->isbusy) { // do nothing here. } // set data first: gadget->data = data; // writing the command starts the action: gadget->command = command; }

Parece fácil, pero puede fallar porque el compilador es libre de cambiar el orden en que se escriben los datos y los comandos. Esto haría que nuestro pequeño gadget emita comandos con el valor de datos anterior. También eche un vistazo a la espera mientras que el ciclo ocupado. Ese será optimizado. El compilador intentará ser inteligente, leer el valor de isbusy solo una vez y luego entrar en un bucle infinito. Eso no es lo que quieres.

La forma de evitar esto es declarar el gadget de puntero como volátil. De esta manera el compilador se ve obligado a hacer lo que usted escribió. No puede eliminar las asignaciones de memoria, no puede almacenar variables en caché en los registros y tampoco puede cambiar el orden de las asignaciones:

Esta es la versión correcta:

void SendCommand (volatile MyHardwareGadget * gadget, int command, int data) { // wait while the gadget is busy: while (gadget->isbusy) { // do nothing here. } // set data first: gadget->data = data; // writing the command starts the action: gadget->command = command; }


no permite que el compilador cambie automáticamente los valores de las variables. Una variable volátil es para uso dinámico.


volátil significa que es probable que el almacenamiento cambie en cualquier momento y se modifique, pero algo fuera del control del programa del usuario. Esto significa que si hace referencia a la variable, el programa siempre debe verificar la dirección física (es decir, una entrada asignada fifo), y no usarla de manera cacheada.


volatile in C realmente entró en existencia con el propósito de no almacenar en caché los valores de la variable automáticamente. Le dirá a la máquina que no almacene en caché el valor de esta variable. Por lo tanto, tomará el valor de la variable volatile dada de la memoria principal cada vez que la encuentre. Este mecanismo se utiliza porque en cualquier momento el valor puede ser modificado por el sistema operativo o cualquier interrupción. Por lo tanto, usar volatile nos ayudará a acceder al valor de nuevo cada vez.


volatile le dice al compilador que su variable puede ser cambiada por otros medios, que el código que lo está accediendo. por ejemplo, puede ser una ubicación de memoria asignada de E / S. Si esto no se especifica en tales casos, algunos accesos variables se pueden optimizar, por ejemplo, su contenido se puede mantener en un registro y la ubicación de la memoria no se puede volver a leer.