[深入浅出C语言]浅析volatile关键字

44 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第6天,点击查看活动详情

前言

        不知道你有没有听说过volatile这么一个关键字,在C中它或许是一个比较冷门的关键字,不过我认为还是需要有所了解的,本文就来简单地讲一下这个关键字(笔者水平较低,可能对你帮助不大)。

        笔者水平有限,难免存在纰漏,欢迎指正交流。

volatile关键字

简介

        volatile关键字和const一样是一种类型限定符,用它修饰的变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其他线程等。遇到这个关键字修饰的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。

        光这样说还是比较抽象的,下面我们来看看一些例子。

举例理解

        举一个不太严谨但好理解一点的例子:

        首先,代码会被编译器进行一定的优化。

        如图,一般来说,每次逻辑判断都要把flag的值从内存中读入CPU的寄存器中,而flag的值在程序执行过程中不会发生改变,这样看来每次都去内存读取就比较麻烦,编译器对此会进行优化,直接把flag的值放在一个寄存器中(“覆盖”内存),每次读取这个寄存器的值即可,显然比去内存读取要快。

        照这样看,如果是单进程的话貌似没什么问题,可要是别的进程中把flag的值改成0的话就不会有响应了,因为编译器优化后不会再读取flag在内存的值了。

        而这个关键字volatile,是不希望代码被编译器优化,以达到稳定访问内存的目的。

        在汇编角度,在Linux平台对比演示一下加和不加volatile的作用,让大家看明白。

不加 volatile

int pass = 1;
int main()
{
    while(pass)
    {}
    return 0;
}

进行如下操作:

[root@VM-0-3-centos code]# gcc test.c -O2 -g //以O2级别进行代码优化
[root@VM-0-3-centos code]# objdump -S -d a.out > a.s //对形成的a.out反汇编重定向到新文件
[root@VM-0-3-centos code]# vim a.s //查看反汇编代码

看看优化后的反汇编代码:

        我们发现,优化后的代码很会“偷懒”,编译器检测到了pass的值为1且无变动,所以知道了这会是一个死循环,于是不再一次一次从内存中读入数值再检测,那得多麻烦不是吗,它就直接自己跳转自己,一直跳也就死循环了。

加上volatile

volatile int pass = 1; 
int main()
{
    while(pass)
    {}
    return 0;
}

进行如下操作:

[root@VM-0-3-centos code]# gcc test.c -O2 -g //以O2级别进行代码优化
[root@VM-0-3-centos code]# objdump -S -d a.out > aa.s //对形成的a.out反汇编重定向到新文件
[root@VM-0-3-centos code]# vim aa.s //查看反汇编代码

看看优化后的反汇编代码:

        volatile 忽略编译器的优化,从而保持内存可见性。

const与volatile的关系

const volatile int a = 10;

        在vs和gcc 4.8中都能编译通过。

        如何解释?

        const是在编译期间起效果,要求你不要进行写入就可以。

        volatile意思是你读取的时候,每次都要从内存读,在编译期间主要影响编译器,形成不优化的代码,进而影响运行。它在编译和运行都起效果。

        这两者并不冲突。

        虽然volatile叫作易变关键字,但这里仅仅是描述它修饰的变量可能会变化,是要编译器注意不要做优化,并不是要求它所对应变量必须变化!这点要特别注意。


以上就是本文全部内容,感谢观看,你的支持就是对我最大的鼓励~

src=http___c-ssl.duitang.com_uploads_item_201708_07_20170807082850_kGsQF.thumb.400_0.gif&refer=http___c-ssl.duitang.gif