持续创作,加速成长!这是我参与「掘金日新计划 · 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叫作易变关键字,但这里仅仅是描述它修饰的变量可能会变化,是要编译器注意不要做优化,并不是要求它所对应变量必须变化!这点要特别注意。
以上就是本文全部内容,感谢观看,你的支持就是对我最大的鼓励~