1. volatile

volatile 单词的意思是“易变的”。在C/C++中它作为关键字修饰变量时,是告诉编译器这个变量可能是会变化的,不要进行优化。比如下面的代码

int some_int = 100;
while(some_int == 100)
{
   //your code
}

编译器发现while(some_int == 100) 中的 some_int 在操作此前一直都是没有变化的。编译器可能会把这条语句优化成 while(true). 而实际的情况是 some_int 可能被其他地方的代码修改值。所以为了保证代码完全是按照我们的意图执行,那么变量可以加上volatile 修饰,告诉编译器这个变量随时可能会变化,不要对使用了这个变量的语句进行优化:

volatile int some_int = 100;

valatile 就是告诉编译器:

“Hey compiler, I’m volatile and, you know, I can be changed by some XYZ that you’re not even aware of. That XYZ could be anything. Maybe some alien outside this planet called program. Maybe some lightning, some form of interrupt, volcanoes, etc can mutate me. Maybe. You never know who is going to change me! So O you ignorant, stop playing an all-knowing god, and don’t dare touch the code where I’m present. Okay?”

​ – why-do-we-use-the-volatile-keyword

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用在如下的几个地方:

1) 中断服务程序中修改的供其它程序检测的变量需要加volatile;

2) 多任务环境下各任务间共享的标志应该加volatile;

3) 存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;

2. volatile 指针

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

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

    const char* cpch;
    volatile char* vpch;
    
  • 指针自身的值——一个代表地址的整数变量,是 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;

(1) 在一个线程中:

 while( !bStop ) { ... } 
 bStop = FALSE; 
 return;  

(2) 在另外一个线程中,要终止上面的线程循环:

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的改变不同步。

参考