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

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


当前回答

volatile关键字的目的是防止编译器对可能以编译器无法确定的方式改变的对象应用任何优化。

声明为volatile的对象在优化中被省略,因为它们的值可以被当前代码范围之外的代码随时更改。系统总是从内存位置读取volatile对象的当前值,而不是在请求时将其值保存在临时寄存器中,即使之前的指令从同一对象中请求一个值。

考虑以下情况

1)由中断服务例程在作用域外修改的全局变量。

2)多线程应用程序中的全局变量。

如果不使用volatile限定符,可能会出现以下问题

1)当优化被打开时,代码可能不会像预期的那样工作。

2)当中断被启用和使用时,代码可能不会像预期的那样工作。

Volatile:程序员最好的朋友

https://en.wikipedia.org/wiki/Volatile_ (computer_programming)

其他回答

In the early days of C, compilers would interpret all actions that read and write lvalues as memory operations, to be performed in the same sequence as the reads and writes appeared in the code. Efficiency could be greatly improved in many cases if compilers were given a certain amount of freedom to re-order and consolidate operations, but there was a problem with this. Even though operations were often specified in a certain order merely because it was necessary to specify them in some order, and thus the programmer picked one of many equally-good alternatives, that wasn't always the case. Sometimes it would be important that certain operations occur in a particular sequence.

Exactly which details of sequencing are important will vary depending upon the target platform and application field. Rather than provide particularly detailed control, the Standard opted for a simple model: if a sequence of accesses are done with lvalues that are not qualified volatile, a compiler may reorder and consolidate them as it sees fit. If an action is done with a volatile-qualified lvalue, a quality implementation should offer whatever additional ordering guarantees might be required by code targeting its intended platform and application field, without requiring that programmers use non-standard syntax.

不幸的是,许多编译器并没有确定程序员需要什么样的保证,而是选择提供标准规定的最低限度的保证。这使得volatile远没有它应有的用处。例如,在gcc或clang上,一个程序员需要实现一个基本的“移交互斥量”(一个已经获得并释放互斥量的任务直到另一个任务释放互斥量后才会再次释放互斥量),他必须做以下四件事中的一件:

将互斥量的获取和释放放在编译器不能内联的函数中,并且不能应用整个程序优化。 将互斥锁保护的所有对象限定为volatile——如果所有访问都发生在获得互斥锁之后和释放互斥锁之前,那么就不应该这样做。 使用优化级别0来强制编译器生成代码,就像所有非限定寄存器的对象都是volatile一样。 使用特定于gcc的指令。

相比之下,当使用更适合系统编程的高质量编译器时,例如icc,人们将有另一种选择:

确保在每个需要获取或释放的地方执行volatile-qualified write。

获取一个基本的“传递互斥量”需要一个volatile读取(看看它是否准备好了),并且不应该需要一个volatile写入(另一方在它被交还之前不会试图重新获取它),但是必须执行一个毫无意义的volatile写入仍然比gcc或clang下可用的任何选项都要好。

您必须使用它来实现自旋锁以及一些(所有?)无锁数据结构 与原子操作/指令一起使用 曾经帮助我克服编译器的错误(在优化过程中错误地生成代码)

我曾经在20世纪90年代早期开发过一个大型应用程序,其中包含使用setjmp和longjmp进行基于c语言的异常处理。volatile关键字对于那些值需要保存在作为“catch”子句的代码块中的变量是必要的,以免这些变量被存储在寄存器中并被longjmp清除。

其他答案已经提到避免一些优化,以便:

使用内存映射寄存器(或MMIO) 写入设备驱动程序 允许更容易的程序调试 使浮点计算更具确定性

当你需要一个值看起来来自外部,不可预测,避免编译器基于已知值进行优化时,当一个结果实际上没有使用,但你需要计算它时,或者当它被使用,但你想要计算它几次作为基准时,你需要计算在精确的点开始和结束时,Volatile是必不可少的。

volatile read类似于输入操作(如scanf或cin的使用):该值似乎来自程序外部,因此任何依赖于该值的计算都需要在它之后开始。

volatile写类似于输出操作(如printf或cout的使用):值似乎是在程序外部传递的,因此如果值依赖于计算,则需要在之前完成。

因此,一对易变的读/写可以用来控制基准测试,使时间测量变得有意义。

如果没有volatile,你的计算可以在编译器之前启动,因为没有什么会阻止计算与时间测量等函数的重新排序。

在实现无锁数据结构时必须使用volatile。否则,编译器可以自由地优化对变量的访问,这将改变语义。

换句话说,volatile告诉编译器对这个变量的访问必须对应于物理内存的读/写操作。

例如,这是Win32 API中InterlockedIncrement的声明方式:

LONG __cdecl InterlockedIncrement(
  __inout  LONG volatile *Addend
);