volatile:
1.readme:修饰符[表示易变的],对于关键字修饰的变量,编译器对访问该变量的代码就不再进行优化[由于可能会被意想不到地改变,OS,多线程,硬件中断等],
从而可以提供对特殊地址的稳定访问。 精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这
2.编译器优化【为了提高效率】:
int a = 2;
int b = a;
。......
int c = a;
当编译器发现b的数值未改变,直接c=b,而不在去取a的数值。或者[内存中有a的数值,编译器不再去读取寄存器,外存a的数值],看来优化也可能出现问题的。
为了使运行更加快速,直接将线程变量[copy到register里,或者只读cache里的数值], 这样外部改变不是反应不了了。【如此优化会很terrible】!!
3.volatile 必要性【例子】:
1.多线程应用中被几个任务共享的变量(share variables);
2.一个中断服务子程序中会访问到的非自动变量(Non-automatic variables);
3.并行设备的硬件寄存器(status register);
嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所以应当考虑何时需要用volatile类型的变量。
4.糟糕的使用 volatile:
这段代码可能有点恶作剧的味道。但它很好说明volatile类型参数的含义和作用。
这段代码的目的是用来返指针*ptr所指向的值的平方,
但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码【虽然看起来,一般状况下,运行结果都OK】:
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;
}
5.volatile 经典问题:
当要求使用volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。
question 1. 一个参数既可以是const还可以是volatile吗?解释为什么。
answer: 可以是。例如对于只读的状态寄存器而言,如果它仅仅是volatile类型,那么它还是可能被意想不到的改变。
但它还是const的时候,程序就不应该试图去修改它
question 2. 一个指针可以是volatile 吗?解释为什么。
answer:可以是的。尽管这种情况并不常见,但它还是可以。一个例子就是:当一个中断服务子程序企图去修改一个指向一个buffer指针的时候。
当然指针也需要存储【内存或者register】,就可能改变。
question3:可能会被编译器不小心优化
1.for ( int i=0; i<100000; i++) ;
2.void interrupt(void) { i=1; }
int main(void)
{
while (1)
{
if (i) dosomething();
}
}
6.[不常见]直接修饰C函数返回值,方便编译器进行优化
我们会在一些代码中用volatile关键字来修饰函数,如linux0.12的源代码中就有这样的语句:
在linux的source code(linux/mm/memory.c)中有这样两句:
volatile void do_exit(long code);
static inline volatile void oom(void)
{
printk("out of memory\n\r");
do_exit(SIGSEGV);
}
那么这里的volatile是什么意思呢?查了很多资料,都是说volatile对变量作用的结果,很少谈及对函数名修饰的作用。
其实这里的作用是帮助编译器进行优化,而不是防止编译器优化,对应oom()和do_exit()函数而言,它们是永远都不会返回的,如果还将调用它们的函数的返回地址保存在堆栈上的话,是没有任何意义的,
但是加了volatile过后,就意味着这个函数不会返回,就相当于告诉编译器,我调用后是不用保存调用我的函数的返回地址的。这样就达到了优化的作用。
这种优化来源于GCC,在GCC2.5版本以后,使用noreturn属性来做优化,如void fatal () __attribute__ ((noreturn));,但是在Gcc2.5的版本以前,没有noreturn属性,所以就用volatile来表示不会返回的函数,以此达到优化的效果。
7.修饰嵌入式汇编,防止编译器进行优化,以及volatile的作用是为了避免函数内部汇编指令的乱序执行。
8.验证volatile的作用
注意,在vc6中,一般调试模式没有进行代码优化,所以这个关键字的作用看不出来。下面通过插入汇编代码,
测试有无volatile关键字,对程序最终代码的影响:
首先,用classwizard建一个win32 console工程,插入一个voltest.cpp文件,输入下面的代码:
#include <stdio.h>
void main()
{
int i=10;
int a = i;
printf("i= %d",a);
//下面汇编语句的作用就是改变内存中i的值,但是又不让编译器知道
__asm {
mov dword ptr [ebp-4], 20h
}
int b = i;
printf("i= %d",b);
}
然后,在调试版本模式运行程序,输出结果如下:
i = 10
i = 32
然后,在release版本模式运行程序,输出结果如下:
i = 10
i = 10
输出的结果明显表明,release模式下,编译器对代码进行了优化,第二次没有输出正确的i值。
下面,我们把 i的声明加上volatile关键字,看看有什么变化:
#include <stdio.h>
void main()
{
volatile int i=10;
int a = i;
printf("i= %d",a);
__asm {
mov dword ptr [ebp-4], 20h
}
int b = i;
printf("i= %d",b);
}
分别在调试版本和release版本运行程序,输出都是:
i = 10
i = 32
这说明这个关键字发挥了它的作用!
参考资料:
1.http://blog.21ic.com/user1/2949/archives/2007/35599.html
2. http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
3. http://www.kernelchina.org/node/618
4.http://drdobbs.com/184401756
5. http://bbs.emath.ac.cn/archiver/tid-3172.html
6.http://www.cnblogs.com/justinzhang/archive/2011/09/28/2194657.html
/-----------------------