1. 为什么用volatile?

C/C++ 中的 volatile 关键字和 const 对应,用来修饰变量,通常用于建立语言级别的 memory barrier。这是 BS 在 "The C++ Programming Language" 对 volatile 修饰词的说明:

A volatile specifier is a hint to a compiler that an object may change its value in ways not specified by the language so that aggressive optimizations must be avoided.

volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。声明时语法:int volatile vInt; 当要求使用 volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。例如:

volatile int i=10;
int a = i;
...
// 其他代码,并未明确告诉编译器,对 i 进行过操作
int b = i;

volatile 指出 i 是随时可能发生变化的,每次使用它的时候必须从 i的地址中读取,因而编译器生成的汇编代码会重新从i的地址读取数据放在 b 中。而优化做法是,由于编译器发现两次从 i读数据的代码之间的代码没有对 i 进行过操作,它会自动把上次读的数据放在 b 中。而不是重新从 i 里面读。这样以来,如果 i是一个寄存器变量或者表示一个端口数据就容易出错,所以说 volatile 可以保证对特殊地址的稳定访问。注意,在 VC 6 中,一般调试模式没有进行代码优化,所以这个关键字的作用看不出来。下面通过插入汇编代码,测试有无 volatile 关键字,对程序最终代码的影响:

输入下面的代码:

#include <stdio.h>void main()
{int i = 10;int a = i;printf("i = %d", a);// 下面汇编语句的作用就是改变内存中 i 的值// 但是又不让编译器知道__asm{mov dword ptr [ebp-4], 20h}int b = i;printf("i = %d", b);
}

然后,在 Debug 版本模式运行程序,输出结果如下:

i = 10
i = 32

然后,在 Release 版本模式运行程序,输出结果如下:

i = 10
i = 10

输出的结果明显表明,Release 模式下,编译器对代码进行了优化,第二次没有输出正确的 i 值。下面,我们把 i 的声明加上 volatile 关键字,看看有什么变化:

#include <stdio.h>void main()
{volatile int i = 10;int a = i;printf("i = %d", a);__asm {mov dword ptr [ebp-4], 20h}int b = i;printf("i = %d", b);
}

分别在 Debug 和 Release 版本运行程序,输出都是:

i = 10
i = 32

这说明这个 volatile 关键字发挥了它的作用。其实不只是“内嵌汇编操纵栈”这种方式属于编译无法识别的变量改变,另外更多的可能是多线程并发访问共享变量时,一个线程改变了变量的值,怎样让改变后的值对其它线程 visible。一般说来,volatile用在如下的几个地方: 
1) 中断服务程序中修改的供其它程序检测的变量需要加volatile; 
2) 多任务环境下各任务间共享的标志应该加volatile; 
3) 存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;


2.volatile 指针

和 const 修饰词类似,const 有常量指针和指针常量的说法,volatile 也有相应的概念:

  • 修饰由指针指向的对象、数据是 const 或 volatile 的:

    const char* cpch;
    volatile char* vpch;

    注意:对于 VC,这个特性实现在 VC 8 之后才是安全的。

  • 指针自身的值——一个代表地址的整数变量,是 const 或 volatile 的:

    char*const pchc;
    char*volatile pchv;

注意:(1) 可以把一个非volatile int赋给volatile int,但是不能把非volatile对象赋给一个volatile对象。

          (2) 除了基本类型外,对用户定义类型也可以用volatile类型进行修饰。
              (3) C++中一个有volatile标识符的类只能访问它接口的子集,一个由类的实现者控制的子集。用户只能用const_cast来获得对类型接口的完全访问。此外,volatile向const一样会从类传递到它的成员。


3. 多线程下的volatile   

有些变量是用volatile关键字声明的。当两个线程都要用到某一个变量且该变量的值会被改变时,应该用volatile声明,该关键字的作用是防止优化编译器把变量从内存装入CPU寄存器中。如果变量被装入寄存器,那么两个线程有可能一个使用内存中的变量,一个使用寄存器中的变量,这会造成程序的错误执行。volatile的意思是让编译器每次操作该变量时一定要从内存中真正取出,而不是使用已经存在寄存器中的值,如下:

volatile  BOOL  bStop  =  FALSE;  // 在一个线程中:  while(  !bStop  )  {  ...  }
bStop  =  FALSE;
return;    //在另外一个线程中,要终止上面的线程循环:
bStop  =  TRUE;
while(  bStop  );  //等待上面的线程终止,

如果bStop不使用volatile申明,那么这个循环将是一个死循环,因为bStop已经读取到了寄存器中,寄存器中bStop的值永远不会变成FALSE,加上volatile,程序在执行时,每次均从内存中读出bStop的值,就不会死循环了。
    这个关键字是用来设定某个对象的存储位置在内存中,而不是寄存器中。因为一般的对象编译器可能会将其的拷贝放在寄存器中用以加快指令的执行速度,例如下段代码中:

int  nMyCounter  =  0;
for(;  nMyCounter<100;nMyCounter++)
{
...
}  

在此段代码中,nMyCounter的拷贝可能存放到某个寄存器中(循环中,对nMyCounter的测试及操作总是对此寄存器中的值进行),但是另外又有段代码执行了这样的操作:nMyCounter  -=  1;这个操作中,对nMyCounter的改变是对内存中的nMyCounter进行操作,于是出现了这样一个现象:nMyCounter的改变不同步。

C/C++中volatile关键字详解相关推荐

  1. java中实现具有传递性吗_Java中volatile关键字详解,jvm内存模型,原子性、可见性、有序性...

    一.Java内存模型 想要理解volatile为什么能确保可见性,就要先理解Java中的内存模型是什么样的. Java内存模型规定了所有的变量都存储在主内存中.每条线程中还有自己的工作内存,线程的工作 ...

  2. java volatile 基本类型_Java中Volatile关键字详解(转)

    一.基本概念 先补充一下概念:Java 内存模型中的可见性.原子性和有序性. 可见性: 可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉.通常,我们无法确保执行读操作的线程能适时地看到其 ...

  3. java中volatile_Java中Volatile关键字详解

    一.基本概念 先补充一下概念:Java并发中的可见性与原子性 可见性: 可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉.通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值, ...

  4. Java中Volatile关键字详解

    https://www.cnblogs.com/zhengbin/p/5654805.html 转载于:https://www.cnblogs.com/wangjing666/p/11301500.h ...

  5. 并发编程系列之volatile关键字详解

    并发编程系列之volatile关键字详解 1.volatile是什么? 首先简单说一下,volatile是什么?volatile是Java中的一个关键字,也是一种同步机制.volatile为了保证变量 ...

  6. java中new关键字详解

    java中new关键字详解 在java中我们可以经常使用new来创建一个对象,但是这对于初学者来说可能只会使用却不能理解new关键字和它的语法 new关键字的语法 注意使用前先导包,一般我们使用ide ...

  7. Java中super关键字详解

    Java中super关键字详解 super有什么用? super什么时候不可以省略呢? super在内存图中是如何存在的呢? super使用时的注意事项 super有什么用? (1)当子类中构造方法第 ...

  8. 在 C/C++中 static 关键字详解

    static 关键字详解 C/C++ 中的 static 1. 静态局部变量 2. 静态全局变量 全局变量 与 extren 3. static 修饰函数 C++的 static 成员 静态成员变量 ...

  9. C++ 中explicit关键字详解

    展开 explicit关键字的作用 使用情况 类型转换函数 单操作数构造函数 同时出现拷贝构造函数和类型转换函数 拷贝构造函数 总结 参考文献 explicit关键字的作用 explicit关键字在写 ...

最新文章

  1. 统计学有多无聊?谷歌统计学家带你证明
  2. 示波器探头×1和×10的意义
  3. Python_问题收录总结
  4. 59. 螺旋矩阵 II(模拟)
  5. SOLID 设计原则
  6. leetcode 347. Top K Frequent Elements | 347. 前 K 个高频元素(大根堆)
  7. CJCMS系列---慢慢讲电子商务在项目中的实践之前言
  8. php网页,想弹出对话框, 消息框 简单代码
  9. 华为手机的硬件好,苹果手机的系统好,能不能将苹果手机的系统装到华为手机里面呢?
  10. 2025 年全球数据量高达175ZB,开发者如何挑战数据洪流?
  11. SQL:统计一个数据库中所有表记录的数量
  12. 环信即时通讯在工程中的安装——Nusen_Liu
  13. 代码管理学:通过文档记录,实现工作传承
  14. 基于stc15f2k60s2芯片单片机编程(按键的长短按)
  15. 论文---基于B/S中小型酒店客房管理系统的设计与实现
  16. UNIX网络编程卷1:套接字联网API(第3版).pdf
  17. 英文书信格式——书写款式(转载)
  18. 红孩儿编输器的模块设计14
  19. Blender Benchmark测试
  20. 程序员不能只会敲代码还要会投资理财

热门文章

  1. altium designer寻找未连接飞线
  2. NR 5G RRC连接重建
  3. rsyslog日志管理+LogAnalyzer
  4. js实现响应式瀑布流
  5. 周日21点50:关注电子阅读的大潮到来
  6. Learn from mistake, i.e. 和 e.g. 是不同的
  7. 使用Vsftpd服务传输文件
  8. 2019.01.07|区块链技术头条
  9. SAP R3 Create Client: T-code:SCC4
  10. mvc 普通上传, 图片转二进制上传