C 参考手册
- C 语言
- C 的历史
- 基本概念
- 表达式
- 声明
- 初始化
- 函数
- 语句
- 静态断言
- 字符常量
- 函数声明
- 函数定义
- 转义序列
- 翻译阶段
- 标识符
- 作用域
- 生存期
- 查找与命名空间
- ASCII 码表
- 类型
- 遵从性
- 算术类型
- restrict 类型限定符
- 类型
- 对象与对齐
- 主函数
- 未定义行为
- 内存模型
- if 语句
- switch 语句
- for 循环
- while 循环
- do-while 循环
- continue语句
- break 语句
- goto语句
- return 语句
- 值类别
- 求值顺序
- 整数常量
- 浮点常量
- 字符串字面量
- 复合字面量
- 常量表达式
- 隐式转换
- 成员访问运算符
- 逻辑运算符
- 比较运算符
- 算术运算符
- 赋值运算符
- 自增/自减运算符
- 其他运算符
- sizeof 运算符
- _Alignof 运算符
- 转型运算符
- C 运算符优先级
- 泛型选择
- 标量初始化
- 数组初始化
- 结构体与联合体初始化
- 指针声明
- 数组声明
- 枚举
- 存储类指定符
- const 类型限定符
- volatile 类型限定符
- 结构体声明
- 联合体声明
- 位域
- _Alignas
- typedef 声明
- 原子类型
- 外部及试探性定义
- inline 函数指定符
- _Noreturn 函数指定符
- 变长参数
- 内联汇编
- 可分析性
- 替用运算符及记号
- C 关键词
- 预处理器
- C 标准库头文件
- 类型支持
- 程序支持工具
- 变参数函数
- 错误处理
- 动态内存管理
- 日期和时间工具
- 字符串库
- 算法
- 数值
- 文件输入/输出
- 本地化支持
- 原子操作库
- 线程支持库
- 实验性 C 标准库
- 有用的资源
- 符号索引
- 注释
volatile 类型限定符
C 类型系统中每一个独立的类型在都有数个该类型的限定版本,对应 const 、 volatile 及限定对于指向对象指针的 restrict 限定符中的一个、两个或全部三个。此页面描述 volatile 限定符的效果。
每一个通过对 volatile 限定类型左值表达式的访问(读与写),对于优化意图都被认为是可观副效应,从而访问会严格按照抽象机的规则求值(即所有写入会在下一个序点之前的某时完成)。这表明在执行的单个线程内,volatile 访问不能被优化掉,亦不能与另一个被顺序点分隔了 volatile 访问的可观副效应重排。
一个非 volatile 值到 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
若数组类型声明具有 volatile 类型限定符(通过使用typedef),则数组类型本身不是 volatile 限定的,但其元素类型是 volatile 限定。若函数类型声明具有 volatile 类型限定(通过使用 typedef ),则行为未定义。
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 f(double x[volatile], const double y[volatile]); void f(double * volatile x, const double * volatile y); |
(C99 起) |
指向非 volatile 类型的指针可以隐式转换成指向同一或兼容类型的 volatile 限定版本的指针。逆向转换可以由类型转换表达式进行。
int* p = 0; volatile int* vp = p; // OK:添加限定符( int 到 volatile int) p = vp; // 错误:丢弃限定符( volatile int 到 int) p = (int*)vp; // OK:类型转换
注意指向T
的二重不可转换成指向volatile T
的二重指针;对于要兼容的两个类型,它们的限定必须相同:
char *p = 0; volatile char **vpp = &p; // 错误: char* 和 volatile char* 不是兼容类型 char * volatile *pvp = &p; // OK,添加限定符( char* 到 char* volatile )
volatile
的用法
volatile
对象模仿映射于内存的 I/O 端口,而 static
const
volatile
对象模仿映射于内存的输入端口,例如实时时钟:
volatile short *ttyport = (volatile short*)TTYPORT_ADDR; for(int i = 0; i < N; ++i) *ttyport = a[i]; // *ttyport 是 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; // 读写非 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) vd += vd*n*m; // 读写 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
引用
- C11 standard (ISO/IEC 9899:2011):
- 6.7.3 Type qualifiers (p: 121-123)
- C99 standard (ISO/IEC 9899:1999):
- 6.7.3 Type qualifiers (p: 108-110)
- C89/C90 standard (ISO/IEC 9899:1990):
- 3.5.3 Type qualifiers