¿Por qué se necesita volátil en C?

10 minutos de lectura

Por que es volatile necesario en C? ¿Para qué se usa esto? ¿Qué hará?

  • http://stackoverflow.com/questions/72552/c-when-has-the-volatile-keyword-ever-helped-you

    – Frederik Slijkermann

    29 de octubre de 2008 a las 8:45

avatar de usuario
Nils Pipenbrinck

volatile le dice al compilador que no optimice nada que tenga que ver con el volatile variable.

Hay al menos tres razones comunes para usarlo, todas relacionadas con situaciones en las que el valor de la variable puede cambiar sin acción desde el código visible: cuando interactúa con hardware que cambia el valor en sí mismo; cuando hay otro subproceso en ejecución que también usa la variable; o cuando hay un controlador de señal que podría cambiar el valor de la variable.

Digamos que tiene una pequeña pieza de hardware que está asignada a la 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 dispositivo emitiera comandos con el valor de datos anterior. También eche un vistazo al bucle de espera mientras está ocupado. Ese será optimizado. El compilador intentará ser inteligente, leer el valor de isBusy solo una vez y luego entra en un ciclo infinito. Eso no es lo que quieres.

La forma de evitar esto es declarar el puntero gadget como volatile. De esta manera, el compilador se ve obligado a hacer lo que escribiste. No puede eliminar las asignaciones de memoria, no puede almacenar en caché las variables 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;
}

  • Personalmente, prefiero que el tamaño entero sea explícito, por ejemplo, int8/int16/int32 cuando hable con el hardware. Aunque buena respuesta 😉

    – Tonylo

    29 de octubre de 2008 a las 8:53

  • sí, debe declarar cosas con un tamaño de registro fijo, pero bueno, es solo un ejemplo.

    – Nils Pipenbrinck

    29 de octubre de 2008 a las 8:54

  • Volatile también es necesario en el código subproceso cuando se juega con datos que no están protegidos por simultaneidad. Y sí, hay momentos válidos para hacerlo, por ejemplo, puede escribir una cola de mensajes circular segura para subprocesos sin necesidad de protección de concurrencia explícita, pero necesitará volátiles.

    -Gordon Wrigley

    29 de octubre de 2008 a las 9:57

  • Lea la especificación C con más atención. Volatile solo tiene un comportamiento definido en la E/S del dispositivo asignado a la memoria o en la memoria tocada por una función de interrupción asíncrona. Dice ninguna cosa sobre subprocesos, y un compilador que optimiza el acceso a la memoria tocado por múltiples subprocesos es conforme.

    – efímero

    29 de octubre de 2008 a las 14:36

  • @tolomea: completamente equivocado. triste 17 personas no lo saben. volátil no es una valla de memoria. solo esta relacionado con evitando la elisión de código durante la optimización basada en la suposición de efectos secundarios no visibles.

    – v.oddou

    13 de septiembre de 2013 a las 3:21

avatar de usuario
cesarb

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

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

El compilador puede notar que el cuerpo del ciclo no toca el quit variable y convertir el bucle en un while (true) círculo. Incluso si los quit variable se establece en el controlador de señal para SIGINT y SIGTERM; el compilador no tiene manera de saber eso.

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

  • cuando dice “el compilador se ve obligado a cargarlo cada vez, es como cuando el compilador decide optimizar una determinada variable y no declaramos la variable como volátil, en tiempo de ejecución esa variable se carga en los registros de la CPU, no en la memoria ?

    – Amit Singh Tomar

    17 de marzo de 2015 a las 16:34

  • @AmitSinghTomar Significa lo que dice: cada vez que el código verifica el valor, se vuelve a cargar. De lo contrario, el compilador puede asumir que las funciones que no toman una referencia a la variable no pueden modificarla, por lo que suponiendo que CesarB pretendía que el bucle anterior no se establece quitel compilador puede optimizarlo en un bucle constante, suponiendo que no hay forma de quit a cambiar entre iteraciones. NB: esto no es necesariamente un buen sustituto de la programación real segura para subprocesos.

    – subrayado_d

    2 de julio de 2016 a las 14:46


  • si quit es una variable global, entonces el compilador no optimizará el ciclo while, ¿correcto?

    – Pierre G.

    20 de agosto de 2016 a las 13:47

  • @PierreG. No, el compilador siempre puede asumir que el código es de un solo subproceso, a menos que se indique lo contrario. Es decir, en ausencia de volatile u otros marcadores, asumirá que nada fuera del ciclo modifica esa variable una vez que ingresa al ciclo, incluso si es una variable global.

    – CesarB

    12/09/2016 a las 12:37

  • @PierreG. Sí, intente, por ejemplo, compilar extern int global; void fn(void) { while (global != 0) { } } con gcc -O3 -S y mire el archivo de ensamblaje resultante, en mi máquina lo hace movl global(%rip), %eax; testl %eax, %eax; je .L1; .L4: jmp .L4, es decir, un bucle infinito si el global no es cero. Luego intente agregar volatile y ver la diferencia.

    – CesarB

    12/09/2016 a las 13:00

avatar de usuario
Manoj Dudas

volatile en C en realidad surgió con el propósito de no almacenar en caché los valores de la variable automáticamente. Le dirá al compilador que no almacene en caché el valor de esta variable. Entonces generará código para tomar el valor de lo dado volatile variable de la memoria principal cada vez que la encuentra. Este mecanismo se utiliza porque en cualquier momento el valor puede ser modificado por el SO o cualquier interrupción. entonces usando volatile nos ayudará a acceder al valor de nuevo cada vez.

  • ¿Llego a existir? ¿No se tomó prestado originalmente ‘volátil’ de C++? Bueno, creo recordar…

    – error de sintaxis

    25 de enero de 2016 a las 4:51

  • Esto no es volátil en absoluto, también prohíbe algunos reordenamientos si se especifica como volátil.

    – FaceBro

    7 junio 2016 a las 14:25

  • @FaceBro: El propósito de volatile fue hacer posible que los compiladores optimicen el código y al mismo tiempo permitir a los programadores lograr la semántica que se lograría sin tales optimizaciones. Los autores del Estándar esperaban que las implementaciones de calidad admitieran cualquier semántica que fuera útil dadas sus plataformas de destino y campos de aplicación, y no esperaban que los escritores de compiladores buscaran ofrecer la semántica de menor calidad que se ajustara al Estándar y no fuera 100% estúpido (tenga en cuenta que los autores de la Norma reconocen explícitamente en la justificación…

    – Super gato

    7 junio 2018 a las 22:43

  • … que es posible que una implementación sea conforme sin tener la calidad suficiente para ser adecuada para cualquier propósito, pero no consideraron necesario evitar eso).

    – Super gato

    7 junio 2018 a las 22:45

  • @syntaxerror, ¿cómo se puede tomar prestado de C++ cuando C era más de una década anterior a C++ (tanto en las primeras versiones como en los primeros estándares)?

    – phuclv

    9 de agosto de 2018 a las 2:28

avatar de usuario
Venkatakrishna Kalepalli

Mi sencilla explicación es:

En algunos escenarios, según la lógica o el código, el compilador optimizará las variables que cree que no cambian. Él volatile La palabra clave evita que se optimice una variable.

Por ejemplo:

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

Del código anterior, el compilador puede pensar 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, lo que resulta en 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 para volátil.

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

avatar de usuario
Super gato

En el lenguaje diseñado por Dennis Ritchie, cada acceso a cualquier objeto, excepto los objetos automáticos cuya dirección no se haya tomado, se comportaría como si calculara la dirección del objeto y luego leyera o escribiera el almacenamiento en esa dirección. Esto hizo que el lenguaje fuera muy poderoso, pero limitaba severamente las oportunidades de optimización.

Si bien podría haber sido posible agregar un calificador que invitaría a un compilador a suponer que un objeto en particular no se cambiaría de manera extraña, tal suposición sería apropiada para la gran mayoría de los objetos en los programas C, y tendría No ha sido práctico agregar un calificador a todos los objetos para los cuales tal suposición sería apropiada. Por otro lado, algunos programas necesitan usar algunos objetos para los cuales tal suposición no se cumpliría. Para resolver este problema, el Estándar dice que los compiladores pueden asumir que los objetos que no están declarados volatile su valor no se observará ni cambiará de manera que esté fuera del control del compilador, o que estaría fuera del entendimiento razonable del compilador.

Debido a que varias plataformas pueden tener diferentes formas en las que los objetos pueden observarse o modificarse fuera del control de un compilador, es apropiado que los compiladores de calidad para esas plataformas difieran en su manejo exacto de volatile semántica. 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 volatile de una manera que reconozca todos y cada uno de los efectos relevantes de una operación particular de lectura/escritura en esa plataforma, muchos compiladores no lo hacen de manera que sea más difícil procesar cosas como E/S en segundo plano de una manera que sea eficiente pero no se puede romper con las “optimizaciones” del compilador.

avatar de usuario
rajeshsam

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

  • ¿Hay algo nuevo en esta respuesta que no se haya mencionado antes?

    – slfan

    22 oct 2018 a las 17:53

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad