ZHCU820Y September 2004 – June 2021
C/C++ 编译器在所有模式下都支持 volatile 关键字,但 。此外,在 C89、C99、C11 和 C++ 的宽松 ANSI 模式中支持 __volatile 关键字。
volatile 关键字向编译器指示该变量的访问方式存在一些要求,要求编译器不要对涉及该变量的表达式使用过于巧妙的优化。例如,外部程序、中断、其他线程或外围设备也可能会访问该变量。
编译器会使用数据流分析来确定访问是否合法,从而尽可能消除冗余的存储器访问。不过,一些存储器访问可能在编译器不明白的某些方面比较特殊,在这类情况下,您应当使用 volatile 关键字来防止编译器优化掉某些重要内容。对于已声明为 volatile 的变量,编译器不会优化掉对该变量的任何访问。volatile 读取和写入的数量将会与 C/C++ 代码中所示的完全相同,不多不少而且顺序也完全相同。
如果任何变量可能由明显程序控制流程外部的东西(例如中断服务例程)进行修改,则必须声明为 volatile。这会告诉编译器,中断函数可能会随时修改该值,因此编译器不应执行会更改该变量的编号或访问顺序的优化。这是 volatile 关键字的主要用途。在以下示例中,循环旨在等待位置作为 0xFF 被读取:
unsigned int *ctrl;
while (*ctrl !=0xFF);
不过,在此示例中,*ctrl 是循环不变量表达式,因此循环会优化为单个存储器读取。若要获取所需结果,应将 ctrl 定义为:
volatile unsigned int *ctrl;
其中,*ctrl 指针旨在引用一个硬件位置,例如中断标志。
访问表示存储器映射外围设备的存储器位置时,也必须使用 volatile 关键字。此类存储器位置可能会以编译器无法预测的方式更改值。这些位置可能会在被访问时、一些其他存储器位置被访问时或者出现某些信号时发生改变。
在会调用 setjmp 的函数中,如果局部变量的值需要在发生 longjmp 时保持有效,则对于局部变量也必须使用 volatile。
#include <stdlib.h>
jmp_buf context;
void function()
{
volatile int x = 3;
switch(setjmp(context))
{
case 0: setup(); break;
default:
{
/* 我们只有在 longjmp 发生时才到达这里。因为 x 的生命周期在 setjmp 之前开始并持续至 longjmp,C 标准要求将 x 声明为 "volatile"。*/
printf("x == %d\n", x);
break;
}
}
}