volatile关键字的作用是什么?在c++中它能解决什么问题?

就我而言,我从来没有明知肚明地需要它。


当前回答

你的程序似乎工作,即使没有挥发关键字?也许这就是原因:

如前所述,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关键字,即使您的程序似乎没有它也可以工作。它使意图在未来的变化方面更加清晰和强大。

其他回答

Dan Saks在《Volatile as a promise》一文中写道:

易失性对象是其值可能自发变化的对象。也就是说,当你声明一个对象为volatile时,你是在告诉编译器这个对象可能会改变状态,即使程序中没有任何语句显示要改变它。”

以下是他关于volatile关键字的三篇文章的链接:

明智地使用挥发剂 准确放置挥发剂 像承诺一样反复无常

在开发嵌入式系统或设备驱动程序时,需要使用Volatile,因为在这些驱动程序中需要读写内存映射的硬件设备。特定设备寄存器的内容随时都可能改变,所以你需要volatile关键字来确保这样的访问不会被编译器优化。

我应该提醒你的一个用法是,在信号处理函数中,如果你想访问/修改一个全局变量(例如,将其标记为exit = true),你必须将该变量声明为'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关键字,即使您的程序似乎没有它也可以工作。它使意图在未来的变化方面更加清晰和强大。