volatile关键字的作用
volatile关键字
volatile关键字是C/C++众多关键字中的一个,但不常用。
我们知道,为了提高程序程序执行的效率。大多数编译器都会对齐进行优化。优化级别由低到高分为:-O0~ -O3。
但有时候,编译器的这种优化也会导致内存和CPU(寄存器)中数据不一致的问题,volatile关键字就是用来解决这种问题的。
Code
接下来我们以linux中的gcc编译器为例,编写一段代码证明volatile关键字的作用。
1.编写一段flag死循环的代码(编译器无优化)
代码:捕捉2号信号并在handler函数里修改flag值
运行程序并CTRL+C捕捉到2号信号时flag置为1,此时flag写入内存,while循环不成立。所以程序退出。
2.编译器优化产生数据二义性问题
代码:用gcc myvol.c -O3命令加上编译器优化,使得main函数中的flag放在寄存器中。
运行程序,由于加了优化,此时另一个执行流handler在内存中更改flag的值编译器就不能察觉了,因为他会直接从寄存器中读数据。所以再次CTRL+C时,程序就不会退出了。
3.volatile取消编译器优化
代码:在程序中加入volatile关键字,取消编译器优化。
运行程序,代码退出。
The End
其实上述问题并非是编译器优化错误。
设想一下,如果程序只有单一的执⾏流程,只要当前执行流程没有改变flag的值,那它就没有理由会变,不需要反复从内存读取。所以编译器优化之后省去了每次循环读内存的操作,效率⾮常⾼。
所以不能说编译器做错了,只能说编译器无法识别程序中存在多个执行流。之所以程序中存在多个执行流程,是因为调用了特定平台上的特定库函数。这些不是C/C++语言本身的规范,不归编译器管,所以不应该编译器来背锅。
程序员应该自己处理这些问题。所以C语⾔提供了volatile限定符,这样即使指定了优化选项,编译器也不会优化掉对变量flag内存单元的读写。
但对于程序中存在多个执行流程访问同一全局变量的情况,volatile限定符是必要的。此外,虽然程序只有单⼀的执行流程,但是变量属于以下情况之一的,也需要volatile限定:
1.变量的内存单元中的数据不需要写操作就可以自⼰发⽣生变化,每次读上来的值都可能不一 样 ,例如接收寄存器
2. 即使多次向变量的内存单元中写数据,只写不读,也并不是在做无用功,而是有特殊意义的。例如发送寄存器。
3. sig_atomic_t类型的变量应该总是加上volatile限定符,因为要使用sig_atomic_t类型的理由也正是要加volatile限定符的理由。