volatile关键字的作用是什么?在c++中它能解决什么问题?
就我而言,我从来没有明知肚明地需要它。
volatile关键字的作用是什么?在c++中它能解决什么问题?
就我而言,我从来没有明知肚明地需要它。
当前回答
我曾经在调试构建中使用过它,当编译器坚持要优化掉一个变量时,我希望在逐步执行代码时能够看到这个变量。
其他回答
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下可用的任何选项都要好。
为嵌入式开发,我有一个循环,检查可以在中断处理程序中更改的变量。如果没有“volatile”,循环就变成了noop——就编译器所知,变量永远不会改变,所以它优化了检查。
同样的事情也适用于在更传统的环境中可能在不同线程中更改的变量,但在那里我们经常进行同步调用,因此编译器在优化方面没有那么自由。
一些处理器具有超过64位精度的浮点寄存器(例如。32位x86没有SSE,见Peter的评论)。这样,如果您对双精度数运行多次操作,实际上会得到比将每个中间结果截断为64位更高精度的答案。
这通常很好,但这意味着根据编译器如何分配寄存器和进行优化,对于完全相同的输入,完全相同的操作将得到不同的结果。如果您需要一致性,那么您可以使用volatile关键字强制每个操作返回内存。
它对于一些没有代数意义但减少浮点误差的算法也很有用,比如Kahan求和。代数上它是一个nop,所以它经常会被错误地优化除非一些中间变量是不稳定的。
我曾经在调试构建中使用过它,当编译器坚持要优化掉一个变量时,我希望在逐步执行代码时能够看到这个变量。
我应该提醒你的一个用法是,在信号处理函数中,如果你想访问/修改一个全局变量(例如,将其标记为exit = true),你必须将该变量声明为'volatile'。