volatile type qualifier
C语言
类型系统
中的每个独立类型都有若干该类型的
限定
版本,对应
const
、
volatile
以及(对于对象指针类型)
restrict
限定符中的一种、两种或全部三种组合。本页描述
volatile
限定符的作用机制。
通过volatile限定类型的左值表达式进行的每次访问(包括读取和写入)均被视为可观察的副作用,出于优化目的,这些访问将严格按照抽象机规则执行(即所有写入操作都必须在下一个序列点之前的某个时刻完成)。这意味着在单线程执行环境中,volatile访问既不能被优化消除,也不能相对于另一个被 序列点 与volatile访问隔开的可见副作用进行重排序。
将非易失性值转换为易失性类型不会产生任何效果。要使用易失性语义访问非易失性对象,必须将其地址转换为指向易失性的指针,然后通过该指针进行访问。
任何尝试通过非volatile左值读写volatile限定类型对象的行为都会导致未定义行为:
volatile int n = 1; // volatile限定类型的对象 int* p = (int*)&n; int val = *p; // 未定义行为
volatile限定结构体或联合体类型的成员会继承其所属类型的限定(无论通过
.
运算符还是
->
运算符访问时均如此):
struct s { int i; const int ci; } s; // s.i 的类型是 int,s.ci 的类型是 const int volatile struct s vs; // vs.i 和 vs.ci 的类型分别是 volatile int 和 const volatile int
|
如果数组类型通过使用
|
(C23 前) |
|
数组类型与其元素类型始终被视为具有相同的 volatile 限定。 |
(C23 起) |
typedef int A[2][3]; volatile A a = {{4, 5, 6}, {7, 8, 9}}; // volatile int 的数组的数组 int* pi = a[0]; // 错误:a[0] 具有 volatile int* 类型 void *unqual_ptr = a; // C23 前有效;C23 起错误 // 注意:clang 即使在 C89-C17 模式下也应用 C++/C23 的规则
如果函数类型被声明为带有 volatile 类型限定符(通过使用
typedef
),其行为是未定义的。
|
在函数声明中,关键字
以下两个声明声明了相同的函数: void f(double x[volatile], const double y[volatile]); void f(double * volatile x, const double * volatile y); |
(since C99) |
指向非 volatile 类型的指针可以隐式转换为指向相同或 兼容类型 的 volatile 限定版本的指针。反向转换需要显式类型转换。
int* p = 0; volatile int* vp = p; // 正确:添加限定符(int 到 volatile int) p = vp; // 错误:丢弃限定符(volatile int 到 int) p = (int*)vp; // 正确:强制类型转换
注意指向
T
的指针不能转换为指向
volatile T
的指针;要使两种类型兼容,它们的限定符必须完全相同:
char *p = 0; volatile char **vpp = &p; // 错误:char* 与 volatile char* 是不兼容的类型 char * volatile *pvp = &p; // 正确,添加限定符(从 char* 到 char*volatile)
目录 |
volatile的用途
static
volatile
对象用于模拟内存映射I/O端口,而
static
const
volatile
对象用于模拟内存映射输入端口(例如实时时钟):
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 变量不适用于线程间通信;它们不提供原子性、同步或内存顺序保证。若对由其他线程修改的 volatile 变量进行读取操作而未进行同步,或存在两个未同步的线程同时修改该变量,由于数据竞争会导致未定义行为。
关键词
示例
演示了使用 volatile 禁用优化的方法
#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); }
可能的输出:
Modified a non-volatile variable 100m times. Time used: 0.00 seconds Modified a volatile variable 100m times. Time used: 0.79 seconds
参考文献
- C17 标准 (ISO/IEC 9899:2018):
-
- 6.7.3 类型限定符 (p: 87-90)
- C11 标准 (ISO/IEC 9899:2011):
-
- 6.7.3 类型限定符 (p: 121-123)
- C99标准(ISO/IEC 9899:1999):
-
- 6.7.3 类型限定符(第108-110页)
- C89/C90 标准 (ISO/IEC 9899:1990):
-
- 6.5.3 类型限定符
参见
|
C++ 文档
关于
cv(
const
与
volatile
)类型限定符
|