对C语言的volatile关键字的理解

    volatile在英语的意思是:挥发性的, 不稳定的, 易变的. 在编程中不是是很容易被理解的它, 加上平常很少被用到, 再加上它更多地被用于硬件编程方面, 所以就更加让一些人琢磨不透了.
    总之, 作为一个变量类型修饰符, volatile的作用就是被设计用来修饰被不同线程访问和修改的变量. 在原子操作中同样会用到. 如果没有它, 很有可能使得编程写的多线程程序出现不可预期的错误, 也可能因为编译器的各种优化而导致编译后的结果表达了不同的意思, 使得程序出现难以发现的错误.
    被volatile修饰的变量是说这个变量可能会被意想不到地被改变, 这样, 编译器就不会在编译会访问该变量的语句的时候, 依然使用保存在某个寄存器的值来加快速度, 取而代之的是, 每次都从该变量的原始地址处重新读取该变量的值, 这样就能使得取到的值总是是"最新"的, 真正意义上的最新. 区别在于:如果编译器在编译涉及到访问某个变量的值的时候, 它会把被频繁访问的变量保存到CPU的寄存器中供复用, 以加快再次访问变量的速度. 但是, 该值是从CPU的寄存器中取出的, 它虽然是最原始的值, 但如果在其它时间, 其它地点的程序如果修改了该变量的值, 那么编译器拿到的值就是一个"过时"的值, 这样就会出现错误. 其它时间可以是CPU的线程调度, 其它地点可以是另一个线程的代码.
    下面看一点C语言例子.

    int main(void)
    {
        int i;
        i = 1;
        i = 2;
        return i;
    }

    在Debug模式(无任何优化)下, 生成的32位汇编代码(省略其它代码)为:

        ; i = 1
        mov dword ptr[ebp-4],1
        ; i = 2
        mov dword ptr[ebp-4],2
        ; return i
        mov eax,dword ptr[ebp-4]
        ret

        可见, 对i的每次赋值都会产生一条汇编语句来对应, 这完全同C程序意思.
    而当生成模式改成Release(优化被打开; 可能需要在项目设置中钩上"生成调试信息")后,对应如下(省略其它代码):

        mov eax,2
        ret

        可见, 多余的赋值代码都被优化掉了, 这就是编译器的优化措施.
    好的, 我们再把源代码改成如下形式:

        int main(void)
        {
            volatile int i;
            i = 1;
            i = 2;
            return i;
        }

    修改后, Debug生成的汇编代码没有变化, 但Release生成的代码却变成了:

        push ecx
        mov dword ptr[esp],1
        mov dword ptr[esp],2
        mov eax,dword ptr[esp]
        pop ecx

        可见, 加了volatile后, 就算打开优化, 编译器也不会作优化, 每次取值都是重新取值, 虽然在这里所有语言直接一条return 2;就可以代替, 但编译器却没有那样做~
    
    
    另一个典型的例子, 可能很多人用于延时,测试速度:

        int i;
        for(i=0; i<10000; i++)
            ;

        在debug模式下, 语句会完完整整地执行10000次. 丝毫不差.
        在release下, 这条语句直接就没有了, 不信的话可以自己试试.
        加上 volatile 后就同debug一样了. 语句才会被执行.
        也就是说, 加上volatile关键字, 相当于程序员告诉编译器:you! 你不能认为在当前程序块中它不会有什么用, 但在其它地方, i的值可能被程序修改, 程序的运行状态跟外界有关, 你不能优化它, 原原本本地执行它!

女孩不哭(QQ:191035066)@2012-12-19 21:18:20 http://www.cnblogs.com/nbsofer


posted @ 2012-12-19 21:18  女孩不哭  阅读(4997)  评论(0编辑  收藏  举报