volatile关键字的作用是什么?在c++中它能解决什么问题?
就我而言,我从来没有明知肚明地需要它。
volatile关键字的作用是什么?在c++中它能解决什么问题?
就我而言,我从来没有明知肚明地需要它。
当前回答
Dan Saks在《Volatile as a promise》一文中写道:
易失性对象是其值可能自发变化的对象。也就是说,当你声明一个对象为volatile时,你是在告诉编译器这个对象可能会改变状态,即使程序中没有任何语句显示要改变它。”
以下是他关于volatile关键字的三篇文章的链接:
明智地使用挥发剂 准确放置挥发剂 像承诺一样反复无常
其他回答
在标准C中,使用volatile的一个地方是在信号处理程序中。事实上,在标准C中,在信号处理程序中可以安全地做的就是修改易失性sig_atomic_t变量,或者快速退出。事实上,AFAIK,这是标准C中唯一需要使用volatile来避免未定义行为的地方。
ISO/IEC 9899:2011 §7.14.1.1 The signal function ¶5 If the signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static or thread storage duration that is not a lock-free atomic object other than by assigning a value to an object declared as volatile sig_atomic_t, or the signal handler calls any function in the standard library other than the abort function, the _Exit function, the quick_exit function, or the signal function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler. Furthermore, if such a call to the signal function results in a SIG_ERR return, the value of errno is indeterminate.252) 252) If any signal is generated by an asynchronous signal handler, the behavior is undefined.
这意味着在标准C中,你可以这样写:
static volatile sig_atomic_t sig_num = 0;
static void sig_handler(int signum)
{
signal(signum, sig_handler);
sig_num = signum;
}
除此之外就没什么了。
POSIX对于在信号处理程序中可以做的事情要宽容得多,但仍然存在限制(其中一个限制是标准I/O库- printf()等-不能安全地使用)。
您必须使用它来实现自旋锁以及一些(所有?)无锁数据结构 与原子操作/指令一起使用 曾经帮助我克服编译器的错误(在优化过程中错误地生成代码)
你的程序似乎工作,即使没有挥发关键字?也许这就是原因:
如前所述,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关键字,即使您的程序似乎没有它也可以工作。它使意图在未来的变化方面更加清晰和强大。
我想引用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告诉编译器对这个变量的访问必须对应于物理内存的读/写操作。
例如,这是Win32 API中InterlockedIncrement的声明方式:
LONG __cdecl InterlockedIncrement(
__inout LONG volatile *Addend
);