c语言,volatile
一、意义:
该关键字的意义就是表示定义的变量值随时都会改变,必须从变量的地址处读取值,所以只有这个变量在使用过程中可能被改变(比如中断程序),就需要用这个关键字说明。
1)volatile,关键字volatile 确保本条指令不会因C 编译器的优化而被省略,且要求每次直接读值。
2)共享的内存地址,多个程序都对它操作的时候。你的程序并不知道,这个内存何时被改变了。如果不加这个voliatile修饰,程序是利用catch当中的数据,那个可能是过时的了,加了 voliatile,就在需要用的时候,程序重新去那个地址去提取,保证是最新的。
—————————————————————————————————————
二、使用场景
volatile 主要用于变量会异步改变的情况下,主要有三个方面
1) . 并行设备的硬件寄存器(cpu外设寄存器)(如:状态寄存器)
2) . 中断和主循环都会用到的全局变量
3).操作系统中的线程间都会用到的公共变量.
———————————————————————————————————
三、嵌入式编程中经常用到 volatile这个关键字,下面是两种用法的例子:[1]
1):告诉compiler不能做任何优化
比如,要往某一地址送两指令:
int *ip =...; //设备地址
*ip = 1; //第一个指令
*ip = 2; //第二个指令
以上程序compiler可能做优化而成:
int *ip = ...;
*ip = 2;
结果第一个指令丢失。如果用volatile, compiler就不允许做任何的优化,从而保证程序的原意:
volatile int *ip = ...;
*ip = 1;
*ip = 2;
即使你要compiler做优化,它也不会把两次付值语句间化为一。它只能做其它的优化。这对device driver程序员很有用。
再比如:[2]
XBYTE[2]=0x55;
XBYTE[2]=0x56;
XBYTE[2]=0x57;
XBYTE[2]=0x58;
如果对外部硬件上述四条语句分别表示不同的操作,会产生四种不同的动作,那么编译器就不能像对待纯粹的程序那样对上述四条语句进行优化只认为XBYTE[2]=0x58,而忽略前三条语句,并且只产生1条机器码。如果XBYTE用volatileo修饰过,此时编译器会逐一的进行编译并产生相应的4条机器码。
2):表示用volatile定义的变量会在程序外被改变,每次都必须从内存中读取,而不能把他放在cache或寄存器中重复使用。
如 volatile char a;
a=0;
while(!a){
//do some things;
}
doother();
如果没有 volatile doother()不会被执行
四、
- volatile与const
volatile和const一起用,没有冲突,const针对本程序而言,保证该值不变;volatile应付其他情况(如多线程,中断),避免优化后出现错误。
- volatile与指针
volatile修饰指针一般用在共享指针上面。
下面代码:
uchar * volatile reg;
行代码里volatile修饰的是reg这个变量。所以这里实际上是定义了一个uchar类型的指针,并且这个指针变量本身是volatile 的。但是指针所指的内容并不是volatile的!在实际使用的时候,编译器对代码中指针变量reg本身的操作不会进行优化,但是对reg所指的内容 reg却会作为non-volatile内容处理,对reg的操作还是会被优化。通常这种写法一般用在对共享指针的声明上,即这个指针变量有可能会被中断等函数修改。将其定义为volatile以后,编译器每次取指针变量的值的时候都会从内存中载入,这样即使这个变量已经被别的程序修改了当前函数用的时候也能得到修改后的值(否则通常只在函数开始取一次放在寄存器里,以后就一直使用寄存器内的副本)。
需要注意将上述代码与下面的代码进行区别
volatile uchar *reg;
这行代码里volatile修饰的是指针所指的内容。所以这里定义了一个uchar类型的指针,并且这个指针指向的是一个volatile的对象。但是指针变量本身并不是volatile的。如果对指针变量reg本身进行计算或者赋值等操作,是可能会被编译器优化的。但是对reg所指向的内容 reg的引用却禁止编译器优化。因为这个指针所指的是一个volatile的对象,所以编译器必须保证对reg的操作都不被优化。通常在驱动程序的开发中,对硬件寄存器指针的定义,都应该采用这种形式。
而
volatile uchar * volatile reg;
这样定义出来的指针就本身是个volatile的变量,又指向了volatile的数据内容。
五、例子:
1)2440里头文件对寄存器地址的定义就是volatile
#define rBWSCON (*(volatile unsigned *)0x48000000) //Bus width & wait status
2)中断程序:
http://www.ednchina.com/ART_58640_29_0_OA_c6686e7a.HTM
如果一个变量会被其它引用改变,或在其它并行的任务中会被改变(例如中断服务程序),都要显式地说明为"volatile",否则在编译器优化阶段会作出错误的判断,例如将这个变量读入寄存器以后,在没有对这个变量赋值以前,会一直使用寄存器中的值,而实际上这个变量的值可能已经被一个指针引用改变了,或者是在中断服务程序中被改变了,下面这个例子说明这种错误:
有一个变量cnt,在定时中断中每隔一个固定时间减一,然后在主程序中等待它减到0。
正确的写法应该是将第一句改为:volatile unsigned char cnt;
需要注意:作为一个健壮的程序,一定要注意volatile的用法,否则即使能得到正确结果,也会给程序移植或升级带来意想不到的问题。
引用:
1. volatile与指针 http://blog.csdn.net/turkeyzhou/article/details/8953911
1、C中的volatile用法 http://www.cnblogs.com/chio/archive/2007/11/24/970632.html
2、Volatile相关知识 http://www.100ask.org/bbs/forum.php?mod=viewthread&tid=3902&highlight=volatile