volatile type qualifier
Cada tipo individual en el
sistema de tipos
de C tiene varias versiones
calificadas
de ese tipo, correspondientes a uno, dos, o los tres calificadores:
const
,
volatile
, y, para punteros a tipos objeto,
restrict
. Esta página describe los efectos del calificador
volatile
.
Cada acceso (tanto lectura como escritura) realizado a través de una expresión lvalue de tipo calificado como volátil se considera un efecto secundario observable para fines de optimización y se evalúa estrictamente de acuerdo con las reglas de la máquina abstracta (es decir, todas las escrituras se completan en algún momento antes del siguiente punto de secuencia). Esto significa que dentro de un único hilo de ejecución, un acceso volátil no puede ser optimizado eliminado o reordenado en relación con otro efecto secundario visible que esté separado por un punto de secuencia del acceso volátil.
Una conversión de un valor no volátil a un tipo volátil no tiene efecto. Para acceder a un objeto no volátil utilizando semántica volátil, su dirección debe convertirse a un puntero-a-volátil y luego el acceso debe realizarse a través de ese puntero.
Cualquier intento de leer o escribir en un objeto cuyo tipo está calificado como volatile a través de un lvalue no volatile resulta en comportamiento indefinido:
volatile int n = 1; // objeto de tipo calificado como volátil int* p = (int*)&n; int val = *p; // comportamiento indefinido
Un miembro de una estructura o unión calificada como volátil adquiere la calificación del tipo al que pertenece (tanto cuando se accede usando el
.
operador como el
->
operador):
struct s { int i; const int ci; } s; // el tipo de s.i es int, el tipo de s.ci es const int volatile struct s vs; // los tipos de vs.i y vs.ci son volatile int y const volatile int
|
Si un tipo de array se declara con el calificador de tipo volatile (mediante el uso de
|
(until C23) |
|
Un tipo de array y su tipo de elemento siempre se consideran idénticamente calificados como volatile. |
(since C23) |
typedef int A[2][3]; volatile A a = {{4, 5, 6}, {7, 8, 9}}; // arreglo de arreglo de volatile int int* pi = a[0]; // Error: a[0] tiene tipo volatile int* void *unqual_ptr = a; // OK hasta C23; error desde C23 // Notas: clang aplica la regla en C++/C23 incluso en modos C89-C17
Si un tipo de función se declara con el calificador de tipo volatile (mediante el uso de
typedef
), el comportamiento es indefinido.
|
En una declaración de función, la palabra clave
Las siguientes dos declaraciones declaran la misma función: void f(double x[volatile], const double y[volatile]); void f(double * volatile x, const double * volatile y); |
(desde C99) |
Un puntero a un tipo no volátil puede convertirse implícitamente a un puntero a la versión calificada como volátil del mismo tipo o de un tipo compatible . La conversión inversa requiere una expresión de conversión explícita (cast).
int* p = 0; volatile int* vp = p; // OK: agrega calificadores (int a volatile int) p = vp; // Error: descarta calificadores (volatile int a int) p = (int*)vp; // OK: conversión
Tenga en cuenta que un puntero a puntero a
T
no es convertible a un puntero a puntero a
volatile T
; para que dos tipos sean compatibles, sus calificaciones deben ser idénticas:
char *p = 0; volatile char **vpp = &p; // Error: char* y volatile char* no son tipos compatibles char * volatile *pvp = &p; // OK, agrega calificadores (char* a char*volatile)
Contenidos |
Usos de volatile
static
volatile
los objetos modelan puertos de E/S mapeados en memoria, y
static
const
volatile
los objetos modelan puertos de entrada mapeados en memoria, como un reloj en tiempo real:
volatile short *ttyport = (volatile short*)TTYPORT_ADDR; for(int i = 0; i < N; ++i) *ttyport = a[i]; // *ttyport is an lvalue of type volatile short
volatile
Las variables que son locales a una función que contiene una invocación de la macro
setjmp
son las únicas variables locales garantizadas para conservar sus valores después de que
longjmp
retorne.
Tenga en cuenta que las variables volátiles no son adecuadas para la comunicación entre hilos; no ofrecen atomicidad, sincronización u ordenamiento de memoria. Una lectura de una variable volátil que es modificada por otro hilo sin sincronización o modificación concurrente desde dos hilos no sincronizados es comportamiento indefinido debido a una carrera de datos.
Palabras clave
Ejemplo
demuestra el uso de volatile para deshabilitar optimizaciones
#include <stdio.h> #include <time.h> int main(void) { clock_t t = clock(); double d = 0.0; for (int n = 0; n < 10000; ++n) for (int m = 0; m < 10000; ++m) d += d * n * m; // reads from and writes to a non-volatile printf("Modified a non-volatile variable 100m times. " "Time used: %.2f seconds\n", (double)(clock() - t)/CLOCKS_PER_SEC); t = clock(); volatile double vd = 0.0; for (int n = 0; n < 10000; ++n) for (int m = 0; m < 10000; ++m) { double prod = vd * n * m; // reads from a volatile vd += prod; // reads from and writes to a volatile } printf("Modified a volatile variable 100m times. " "Time used: %.2f seconds\n", (double)(clock() - t)/CLOCKS_PER_SEC); }
Salida posible:
Modified a non-volatile variable 100m times. Time used: 0.00 seconds Modified a volatile variable 100m times. Time used: 0.79 seconds
Referencias
- Estándar C17 (ISO/IEC 9899:2018):
-
- 6.7.3 Calificadores de tipo (p: 87-90)
- Estándar C11 (ISO/IEC 9899:2011):
-
- 6.7.3 Calificadores de tipo (p: 121-123)
- Estándar C99 (ISO/IEC 9899:1999):
-
- 6.7.3 Calificadores de tipo (p: 108-110)
- Estándar C89/C90 (ISO/IEC 9899:1990):
-
- 6.5.3 Calificadores de tipo
Véase también
|
Documentación de C++
para
calificadores de tipo cv (
const
y
volatile
)
|