标签:
在编译器编译代码时候,一般都会作些减少存取内存的优化,但有可能会读脏数据,比如int *ip =...; //设备地址
*ip = 1; //第一个指令
*ip = 2; //第二个指令
以上程序compiler可能做优化而成:
int *ip = ...;
*ip = 2;
结果第一个指令丢失。
但有的情况下,特别是硬件开发方面,在针对某些变量不希望编译器做内存优化。因而引入了volatile。
volatile的本意是“易变的”。当使用volatile声明变量值的时候,即使它前面的指令刚刚从该处读取或者是写过数据,系统总是重新从它所在的内存读取或者是写数据,也就是说编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问;如果不使用valatile,则编译器将对所声明的语句进行优化。(简洁的说就是:volatile关键词表示该变量随时可能发生变化,与该变量有关的运算,不要进行编译优化,以免出错,影响编译器编译的结果)
像上面的例子, 如果用volatile, compiler就不允许做任何的优化,从而保证程序的原意,执行的语句还是:
volatile int *ip = ...;
*ip = 1;
*ip = 2;
下面是使用volatile变量的几个场景:
1>中断服务程序中修改的供其它程序检测的变量需要加volatile;
2>多任务环境下各任务间共享的标志应该加volatile
3>存储器映射的硬件寄存器通常也要加voliate,因为每次对它的读写都可能有不同意义。
例如:假设要对一个设备进行初始化,此设备的某一个寄存器为0xff800000。
int *output = (unsigned int *)0xff800000;//定义一个IO端口;
int init(void)
{
int i;
for(i=0;i< 10;i++){
*output = i;
}
}
经过编译器优化后,编译器认为前面循环半天都是废话,对最后的结果毫无影响,因为最终只是将output这个指针赋值为9,所以编译器最后给你编译编译的代码结果相当于:
int init(void)
{
*output = 9;
}
如果你对此外部设备进行初始化的过程是必须是像上面代码一样顺序的对其赋值,显然优化过程并不能达到目的。这时候就该使用volatile通知编译器这个变量是一个不稳定的,在遇到此变量时候不要优化。
例如:
volatile int *output=(volatile unsigned int *)0xff800000;//定义一个I/O端口
int init(void)
{
int i;
for(i=0;i< 10;i++){
*output = i;
}
}
再比如说,下面的函数有什么错误:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
该程序的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
注意:频繁地使用volatile很可能会增加代码尺寸和降低性能,因此要合理的使用volatile
标签:
原文地址:http://www.cnblogs.com/Hailong-Said/p/4848599.html