《C语言语法知识》-- volatile关键字

就像大家更熟悉的const一样,volatile是一个类型修饰符(type specifier)。它是被设计用来修饰被不同线程访问和修改的变量。
volatile的作用是: 作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。
简单地说就是防止编译器对代码进行优化。比如如下程序:

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

对外部硬件而言,上述四条语句分别表示不同的操作,会产生四种不同的动作,但是编译器却会对上述四条语句进行优化,认为只有XBYTE[2]=0x58(即忽略前三条语句,只产生一条机器代码)。如果键入volatile,则编译器会逐一的进行编译并产生相应的机器代码(产生四条代码)。

再如以下程序:

volatile int i=10;
int a=i;
//...
//其他代码,并未明确告诉编译器,对i进行过操作
int b=i;

volatile 指出 i 是随时可能发生变化的,每次使用它的时候必须从 i 的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在 b 中。
而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对 i 进行过操作,它会自动把上次读的数据放在 b 中(这时就用到了寄存器)。而不是重新从 i 里面读。
这样一来,如果 i 是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问。

volatile代表优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。

详细的说明就是:
1. 在本次线程内,当读取一个变量时,为提高存取速度,编译器优化时有时会先把变量读取到一个寄存器中;以后再取变量值时,就直接从寄存器中取值;
2. 当变量值在本线程里改变时,会同时把变量的新值copy到该寄存器中,以便保持一致。
3. 当变量在因别的线程等而改变了值,该寄存器的值不会相应改变,从而造成应用程序读取的值和实际的变量值不一致。
4. 当该寄存器在因别的线程等而改变了值,原变量的值不会改变,从而造成应用程序读取的值和实际的变量值不一致。

下面是volatile变量的几个例子:
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能有不同意义;
另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2 中可以禁止任务调度,3中则只能依靠硬件的良好设计了。

问:那么一个参数既可以是const还可以是volatile吗?
答:是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。

最后举一个我再别的地方看到的例子:

short flag;
void test()
{
do1();
while(flag==0);
do2();
}

在DSP开发中,经常需要等待某个事件的触发,所以经常会写出这样的程序,这段程序等待内存变量flag的值变为1之后才运行do2( )。变量flag的值由别的程序更改,这个程序可能是某个硬件中断服务程序。
例如:如果某个按钮按下的话,就会对DSP产生中断,在按键中断程序中修改flag为1,这样上面的程序就能够得以继续运行。但是,编译器并不知道flag的值会被别的程序修改,因此在它进行优化的时候,可能会把flag的值先读入某个寄存器,然后等待那个寄存器变为1。
如果不幸进行了这样的优化,那么while循环就变成了死循环,因为寄存器的内容不可能被中断服务程序修改。为了让程序每次都读取真正flag变量的值,就需要定义为如下形式:

volatile short flag;

需要注意的是,没有volatile也可能能正常运行,但是可能修改了编译器的优化级别之后就又不能正常运行了。因此经常会出现debug版本正常,但是release版本却不能正常的问题。所以为了安全起见,只要是等待别的程序修改某个变量的话,就加上volatile关键字。

最后还要补充一点,就是对volatile变量的读写不会被优化掉。如果你对一个变量赋值但后面没用到,编译器常常可以省略那个赋值操作,然而对Memory Mapped IO等的处理是不能这样优化的。

就像我在文《Kinetis单片机——结构体相关》中的那个结构体定义中所示:

typedef struct UART0_MemMap {
//...
//硬件映射的相关寄存器
} volatile *UART0_MemMapPtr;

volatile *UART0_MemMapPtr;表示声明一个指向volatile型变量的指针,即该结构体中的寄存器都定义为volatile型的,这种用法常用于单片机头文件中定义寄存器使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值