volatile关键字

首先简单介绍一下编译器对代码优化的概念:
编译器优化:在不影响程序结果的情况下,改变程序的执行顺序提高效率
优化级别有:
O0 O1 O2 O3
优先级别越高,优化的越厉害
如何优化?在此介绍volatile,我们只谈优化的一个方式,就是将频繁使用的变量直接加载到离cpu很近的寄存器中。

我们先来看如下代码:

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
int flag=1;
void Handler(int signo){
printf("signo=%d\n",signo);
flag=0;
}
int main(){
signal(2,Handler);
while(flag){}                                                                                                        
}

在不优化的情况下直接进行编译,我们可预见:程序运行起来,当给这个进程发送二号信号flag值才会变为0使循环结束程序运行结束。
但当用O2使编译器对这个代码进行优化时,就会发现按下ctrl+c发送2号信号时,循环依旧不会停止。这是为什么呢?
原来:
在编译器在优化过程中,若编译器判定某个数据是一个比较高的开销,然后编译器没有检测到有代码修改这个数据,便会把频繁使用的数据放到了寄存器中(while循环频繁使用flag,Handle函数虽对他进行修改但是由内核调用的,编译器并不知道),编译器就可能作出了错误的判断,这时就直接把flag这个值优化到寄存器里了,Handle函数对flag的修改只是改变了内存中的flag并没有改变寄存器中的flag,因而while判断时用到寄存器中的flag一直是1.所以循环就结束不了。
为了避免这种编译器的错误决措,我们引入volatile关键字
这个关键字修饰变量就是告诉编译器,这个变量必须每次都从内存中读,不敢直接加载到寄存器中,即volatile的目的就是保持内存可见性

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
volatile int flag=1;
void Handler(int signo){
printf("signo=%d\n",signo);
flag=0;
}
int main(){
signal(2,Handler);
while(flag){}                                                                                                        
}

在flag前加上volatile,这时候不管怎么优化flag都是从内存中读取的,一改变他就可以读入新的值,因而这个程序当接收到2信号时就可以正常退出了。

volatile要经常使用在多线程中,因为编译器对于多执行流的情况不太会判断,所以volatile经常要使用在多线程来让cpu用的变量都是新的。

与volatile相对的是register,即告诉编译器把这个变量放到寄存器中。
但是这register个关键字不经常用了因为编译器知道什么变量该放什么不该放,会自动优化。

相关推荐