La palabra clave volátil en lenguaje C

Volatile es un especificador de tipo, al igual que el familiar const, está diseñado para modificar variables a las que se accede y modifican diferentes subprocesos; volatile se usa como una palabra clave de instrucción para garantizar que esta instrucción no se vea afectada por la omisión de la optimización del compilador, y el valor debe leerse directamente cada vez.

Una variable volátil significa que la variable puede cambiarse inesperadamente, por lo que el compilador no asumirá el valor de esta variable.

efecto

En pocas palabras, es para evitar que el compilador optimice el código. Por ejemplo, el siguiente programa:

XBYTE[2]=0x55;
XBYTE[2]=0x56;
XBYTE[2]=0x57;
XBYTE[2]=0x58;

Para hardware externo, las cuatro declaraciones anteriores representan respectivamente operaciones diferentes y producirán cuatro acciones diferentes, pero el compilador optimizará las cuatro declaraciones anteriores, pensando que solo XBYTE [2] = 0x58 (es decir, ignora las tres primeras declaraciones, Solo una se genera el código de máquina). Si escribe volatile, el compilador compilará uno por uno y generará el código de máquina correspondiente (se generan cuatro códigos).
Para ser precisos, el optimizador debe releer cuidadosamente el valor de esta variable cada vez que usa esta variable, en lugar de usar la copia de seguridad almacenada en el registro. A continuación, se muestran algunos ejemplos de variables volátiles:

1) Registros de hardware de dispositivos paralelos (como registros de estado)

2) Variables no automáticas a las que se accederá en una subrutina de servicio de interrupción (Variables no automáticas)

3) Variables compartidas por varias tareas en aplicaciones multiproceso

Este es el problema más básico que distingue a los programadores de C de los programadores de sistemas integrados: los programadores de sistemas integrados a menudo tratan con hardware, interrupciones, RTOS, etc., todos los cuales requieren el uso de variables volátiles. No comprender el contenido volátil traerá un desastre.

Suponiendo que el entrevistado responda esta pregunta correctamente (bueno, me pregunto si este es el caso), profundizaré un poco más para ver si este tipo realmente entiende la importancia de lo volátil.

1) ¿Puede un parámetro ser constante o volátil? Explicar por qué.

2) ¿Puede un puntero ser volátil? Explicar por qué.

3) La siguiente función se utiliza para calcular el cuadrado de un determinado número entero ¿Puede lograr el objetivo de diseño esperado? Si no es así, intente responder la pregunta:

int square(volatile int *ptr)

{

    return ((*ptr) * (*ptr));

}

Esta es la respuesta:

1) Sí. Un ejemplo es el registro de estado de solo lectura. Es volátil porque se puede cambiar inesperadamente. Es constante porque el programa no debería intentar modificarlo.

2) Sí. Aunque esto no es muy común. Un ejemplo es cuando una rutina de servicio de interrupción modifica un puntero a un búfer.

3) Este código es una broma. El propósito de este código es devolver el cuadrado del valor al que apunta el puntero ptr.Sin embargo, dado que ptr apunta a un parámetro volátil, el compilador generará un código similar al siguiente:

int square(volatile int* &ptr)//这里参数应该申明为引用,不然函数体里只会使用副本,外部没法更改

{

    int a,b;

    a = *ptr;

    b = *ptr;

    return a*b;

}

Dado que el valor de * ptr puede cambiar entre las dos declaraciones de valor, ayb pueden ser diferentes. Como resultado, es posible que este código no devuelva el valor cuadrado que esperaba. El código correcto es el siguiente:

long square(volatile int*ptr)

{

    int a;

    a = *ptr;

    return a*a;

}

Hable sobre la comprensión personal:

La clave está en dos lugares:

⒈Optimización del compilador (ayúdeme a ver el entendimiento a continuación)

En este hilo, al leer una variable, para mejorar la velocidad de acceso, el compilador a veces lee la variable en un registro al optimizar, cuando el valor de la variable se recupera más tarde, el valor se recupera directamente del registro;

Cuando se cambia el valor de la variable en este hilo, el nuevo valor de la variable se copiará al registro al mismo tiempo, para mantener la coherencia.

Cuando el valor de la variable cambia debido a otros subprocesos, etc., el valor del registro no cambiará en consecuencia, lo que hará que el valor leído por el programa de aplicación sea inconsistente con el valor real de la variable.

Cuando el valor del registro cambia debido a otros subprocesos, etc., el valor de la variable original no cambiará, lo que resultará en una inconsistencia entre el valor leído por la aplicación y el valor real de la variable.

Dé un ejemplo inexacto:

Al pagar los salarios, el contador llama a los empleados para registrar su número de tarjeta bancaria cada vez; con el fin de evitar problemas, el contador no se registró de inmediato y usó el número de tarjeta bancaria previamente registrado; solo un empleado perdió su tarjeta bancaria y el banco se ha informado el número de tarjeta; como resultado, el empleado no puede recibir salarios

Dirección variable original del empleado

Número de tarjeta bancaria: la copia de seguridad de la variable original en el registro.

⒉ ¿En qué circunstancias aparecerá

1) Registros de hardware de dispositivos paralelos

2) Variables no automáticas a las que se accederá en una subrutina de servicio de interrupción (Variables no automáticas)

3) Variables compartidas por varias tareas en aplicaciones multiproceso

Suplemento: Volátil debe interpretarse como "acceso directo a la dirección de memoria original" es más apropiado, la interpretación de "volátil" es simplemente engañosa;

La "variabilidad" es causada por factores externos, como multiproceso, interrupciones, etc. No es porque la variable modificada con volátil sea "volátil". Si no hay una causa externa, incluso si se usa la definición volátil, no cambiará ;

Después de usar la definición volátil, de hecho, esta variable no cambiará por razones externas, por lo que puede usarla con confianza; veamos si la explicación anterior (modificable) es engañosa.

La palabra clave volátil es un modificador de tipo. La variable de tipo declarada con ella puede ser cambiada por algunos factores desconocidos para el compilador, como el sistema operativo, hardware u otros subprocesos. Al encontrar la variable declarada por esta palabra clave, el compilador ya no optimizará el código que accede a la variable, de modo que pueda proporcionar un acceso estable a la dirección especial.

Los ejemplos de uso de esta palabra clave son los siguientes:

volatile int vint;

Cuando se requiere el valor de una variable declarada por volatile, el sistema siempre lee datos de la memoria donde se encuentra, incluso si la instrucción anterior acaba de leer datos de ella. Y los datos leídos se guardan de inmediato.

P.ej:

volatile int i=10;

int a=i;

//...

// Otro código, no le dijo explícitamente al compilador, ha operado en i

int b=i;

Volatile señala que i puede cambiar en cualquier momento, y debe leerse desde la dirección de i cada vez que se use. Por lo tanto, el código ensamblador generado por el compilador leerá los datos de la dirección de i nuevamente y los colocará en b. El método de optimización es que dado que el compilador encuentra que el código entre los dos códigos que leen datos de i no ha operado en i, automáticamente colocará los datos leídos la última vez en b. En lugar de volver a leer de i. De esta manera, si i es una variable de registro o representa un dato de puerto, es propenso a errores, por lo que la volatilidad puede garantizar un acceso estable a direcciones especiales.

Tenga en cuenta que en vc6, la optimización del código no se realiza en el modo de depuración general, por lo que el efecto de esta palabra clave no es visible. A continuación, inserte el código ensamblador para probar si hay una palabra clave volátil y su impacto en el código final del programa:

Primero, use classwizard para construir un proyecto de consola win32, inserte un archivo voltest.cpp e ingrese el siguiente código:

#include<stdio.h>

 

void main(int argc,char *argv[])

 

{

     

    int i = 10;

     

    int a = i;

     

    printf("i=%d",a);

    //下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道

    __asm

    {

        mov dword ptr[ebp-4],20h

    }

    int b = i;

    printf("i=%d",b);

}

Luego, ejecute el programa en modo de versión de depuración, y los resultados de salida son los siguientes:

i = 10

i = 32

Luego, ejecute el programa en el modo de versión de lanzamiento y los resultados de salida son los siguientes:

i = 10

i = 10

El resultado de salida muestra claramente que en el modo de lanzamiento, el compilador optimizó el código y no emitió el valor i correcto la segunda vez. A continuación, agregamos la palabra clave volátil a la declaración de i para ver qué cambia:

#include<stdio.h>

void main(int argc,char *argv[])

{

    volatile int i = 10;

    int a = i;

    printf("i=%d",a);

    __asm

    {

    `    mov dword ptr[ebp-4],20h

    }

    int b = i;

    printf("i=%d",b);

}

Ejecute el programa en la versión de depuración y la versión de lanzamiento respectivamente, el resultado es:

i = 10

i = 32

¡Esto muestra que esta palabra clave ha jugado su papel!
------------------------------------

La variable correspondiente a volátil puede cambiar sin que su programa lo sepa.

Por ejemplo, en un programa multiproceso, varios programas pueden manipular esta variable en la memoria compartida.

Su propio programa no puede determinar cuándo cambiará esta variable

Por otro ejemplo, corresponde a un cierto estado de un dispositivo externo.Cuando se opera el dispositivo externo, el sistema cambia el valor de esta variable a través del controlador e interrumpe los eventos, pero su programa no lo sabe.

Para una variable volátil, cada vez que el sistema la usa, la extrae directamente de la memoria correspondiente, en lugar de usar el valor original en la caché para adaptarse a la incógnita cuando cambiará. No se optimizará el procesamiento de esta variable. obviamente porque su valor puede cambiar en cualquier momento.


Supongo que te gusta

Origin blog.csdn.net/weixin_43704402/article/details/114131627
Recomendado
Clasificación