volatile关键字的作用是什么?在c++中它能解决什么问题?
就我而言,我从来没有明知肚明地需要它。
volatile关键字的作用是什么?在c++中它能解决什么问题?
就我而言,我从来没有明知肚明地需要它。
当前回答
在开发嵌入式系统或设备驱动程序时,需要使用Volatile,因为在这些驱动程序中需要读写内存映射的硬件设备。特定设备寄存器的内容随时都可能改变,所以你需要volatile关键字来确保这样的访问不会被编译器优化。
其他回答
在实现无锁数据结构时必须使用volatile。否则,编译器可以自由地优化对变量的访问,这将改变语义。
换句话说,volatile告诉编译器对这个变量的访问必须对应于物理内存的读/写操作。
例如,这是Win32 API中InterlockedIncrement的声明方式:
LONG __cdecl InterlockedIncrement(
__inout LONG volatile *Addend
);
其他答案已经提到避免一些优化,以便:
使用内存映射寄存器(或MMIO) 写入设备驱动程序 允许更容易的程序调试 使浮点计算更具确定性
当你需要一个值看起来来自外部,不可预测,避免编译器基于已知值进行优化时,当一个结果实际上没有使用,但你需要计算它时,或者当它被使用,但你想要计算它几次作为基准时,你需要计算在精确的点开始和结束时,Volatile是必不可少的。
volatile read类似于输入操作(如scanf或cin的使用):该值似乎来自程序外部,因此任何依赖于该值的计算都需要在它之后开始。
volatile写类似于输出操作(如printf或cout的使用):值似乎是在程序外部传递的,因此如果值依赖于计算,则需要在之前完成。
因此,一对易变的读/写可以用来控制基准测试,使时间测量变得有意义。
如果没有volatile,你的计算可以在编译器之前启动,因为没有什么会阻止计算与时间测量等函数的重新排序。
我想引用Herb Sutter在GotW #95中的一句话,这有助于理解volatile变量的含义:
C++ volatile variables (which have no analog in languages like C# and Java) are always beyond the scope of this and any other article about the memory model and synchronization. That’s because C++ volatile variables aren’t about threads or communication at all and don’t interact with those things. Rather, a C++ volatile variable should be viewed as portal into a different universe beyond the language — a memory location that by definition does not obey the language’s memory model because that memory location is accessed by hardware (e.g., written to by a daughter card), have more than one address, or is otherwise “strange” and beyond the language. So C++ volatile variables are universally an exception to every guideline about synchronization because are always inherently “racy” and unsynchronizable using the normal tools (mutexes, atomics, etc.) and more generally exist outside all normal of the language and compiler including that they generally cannot be optimized by the compiler (because the compiler isn’t allowed to know their semantics; a volatile int vi; may not behave anything like a normal int, and you can’t even assume that code like vi = 5; int read_back = vi; is guaranteed to result in read_back == 5, or that code like int i = vi; int j = vi; that reads vi twice will result in i == j which will not be true if vi is a hardware counter for example).
你的程序似乎工作,即使没有挥发关键字?也许这就是原因:
如前所述,volatile关键字有助于以下情况
volatile int* p = ...; // point to some memory
while( *p!=0 ) {} // loop until the memory becomes zero
但是,一旦调用外部函数或非内联函数,似乎几乎没有任何影响。例如:
while( *p!=0 ) { g(); }
然后无论是否使用volatile都会产生几乎相同的结果。
只要g()可以完全内联,编译器就可以看到正在发生的一切,因此可以进行优化。但是,当程序调用一个编译器看不到发生什么的地方时,编译器再做任何假设就不安全了。因此,编译器生成的代码总是直接从内存中读取。
但是要注意,当函数g()变成内联(由于显式更改或由于编译器/链接器的聪明)时,如果您忘记volatile关键字,那么您的代码可能会崩溃!
因此,我建议添加volatile关键字,即使您的程序似乎没有它也可以工作。它使意图在未来的变化方面更加清晰和强大。
除了volatile关键字用于告诉编译器不要优化对某些变量(可以由线程或中断例程修改)的访问之外,它还可以用于删除一些编译器错误——是的,它可以是——。
例如,我在一个嵌入式平台上工作,编译器对变量的值做出了一些错误的假设。如果代码没有优化,程序可以正常运行。通过优化(这是非常必要的,因为这是一个关键的例程),代码将无法正常工作。唯一的解决方案(虽然不是很正确)是将“错误的”变量声明为volatile。