对volatile修饰符的理解和使用

来源:互联网 发布:阿里云 禁止ip访问 编辑:程序博客网 时间:2024/06/18 16:35

1. 什么是volatile修饰符?

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

2. 一个简单的例子

volatile 影响编译器编译的结果。volatile变量是随时可能发生变化的,与volatile变量有关的运算,不进行编译优化。如:

[cpp] view plaincopyprint?
  1. volatile int i=10;  
  2. int j = i;  
  3. …  
  4. …  
  5. int k = i;  

不优化的方式:编译器生成的可执行码会重新从i的地址读取数据放在k中
优化的方式:由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中。

也就是说:volatile 告诉编译器i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的可执行码会重新从i的地址读取数据放在k中。
而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中。而不是重新从i里面读。这样以来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问,不会出错。

下面是一个实例,使用(VC6):

[cpp] view plaincopyprint?
  1. #include <stdio.h>   
  2.   
  3. int main()  
  4. {  
  5.     int i = 10;  
  6.     int b;  
  7.     int a = i;  
  8.     printf("i = %d\n", a);  
  9.     // 下面汇编语句的作用就是改变内存中i的值   
  10.     __asm  
  11.     {  
  12.         mov dword ptr [ebp-4], 20h  
  13.     }  
  14.     b = i;  
  15.     printf("i = %d\n", b);  
  16.     return 0;  
  17. }  

大家在编译的时候选择debug和release模式会得到不同的结果。其中debug意味着不进行优化,release则编译器进行优化。有图有真相:

3. 寄存器的那些事

寄存器是CPU内部用来存放数据的一些小型存储区域,用来暂时存放参与运算的数据和运算结果。
存取速度:寄存器 >> cache >> RAM
在目前的计算机技术中,主存储器存取速度比CPU操作慢很多。寄存器可以充分发挥CPU高速处理的性能,缓和中央处理器和主存储器之间速度不匹配的矛盾。
寄存器中变量的状态并不能直接等同于内存中此变量的状态

4. 多线程共享变量

Thread A:                   ThreadB:
int k;                   ...
gTest = 1;                   ...
...                        ...
...                        gTest= 2;
...                        ...
k = gTest;          ...

在本次线程内, 当读取一个变量时,为提高存取速度,编译器优化时有时会先把变量读取到一个寄存器中;以后,再取变量值时,就直接从寄存器中取值;当变量值在本线程里改变时,会同时把变量的新值copy到该寄存器中,以便保持一致。当变量在因别的线程等而改变了值,该寄存器的值不会相应改变,从而造成应用程序读取的值和实际的变量值不一致。

5. ISR修改变量

[cpp] view plaincopyprint?
  1. static int i = 0;  
  2. int main()  
  3. {   …  
  4.     while(1)  
  5.     { if(i) dosomething(); }  
  6. }  
  7. /* Interrupt service routine. */  
  8. void ISR(void)  
  9. { i = 1; }  

由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如:程序的本意是希望ISR中断产生时,在main当中调用dosomething函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致dosomething永远也不会被调用。如果将变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。

6. 在何时使用volatile?

volatile:”易失变量“? ”编译时不做优化“、”直接存取原始内存地址“

一般说来,volatile用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器(如状态寄存器)通常也要加volatile说明,因为每次对它的读写都可能有不同意义

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 微信被限制登录收不到验证码怎么办 微信登录申诉收不到短信怎么办 登录微信手机收不到验证码怎么办 美图t8冲一会电就发烧怎么办 美图t8系统不小心升级了 怎么办 美图手机看相册视频就会变黑怎么办 苹果4s屏幕唤醒速度慢怎么办 美图m4换电池后不显示卡怎么办 美图手机久没充电再充没反应怎么办 美图6s开不开机怎么办 苹果4s照片不能拍照黑屏怎么办 美图手机开机键坏了怎么办 金立金刚二手机烧卡怎么办 金立金刚手机开不开机怎么办 小米4手机拆机信号不好怎么办 荣耀自带游览器无法正常打开怎么办 苹果4s电池越来越不耐用怎么办 苹果4s电池不耐用了怎么办 苹果手机4s电池不耐用怎么办 红米4s电池不耐用怎么办 32位app私密相册打不开怎么办 红米手机取卡针断手机里了怎么办 我差评了客服打电话骂我怎么办 顺丰快递在预计时间没有回来怎么办 同款商品比京东便宜怎么办 京东自营不支持7天退货怎么办 天猫买了一个月的电动车坏了怎么办 发票号码和机打号码不一致怎么办 交电费的本子弄丢了怎么办 快递正在派件中发现地址错了怎么办 快递当天送达当天签收还算延怎么办 深圳国税公众号预约取号公司怎么办 社保买了停了2年怎么办 qq号被冻结申请不回来了怎么办 qq号被冻结 手机密保忘了怎么办 微信账号封了2天怎么办 买qq号被申诉找回了怎么办 收到了京东白条的催款通知单怎么办 成都买房社保不够两年怎么办18年 电话号码给人设置成骚扰电话怎么办 找不到领导电话不接短信不回怎么办