为什么C中需要挥发性?它的用途是什么?它会做什么?
当前回答
维基上有关于volatile的所有信息:
Volatile(计算机编程)
Linux内核的文档也对volatile做了一个很好的标记:
为什么不应该使用“volatile”类型类
其他回答
在Dennis Ritchie设计的语言中,除了地址未被获取的自动对象外,对任何对象的每次访问都表现为计算对象的地址,然后在该地址上读写存储。这使得该语言非常强大,但严重限制了优化机会。
While it might have been possible to add a qualifier that would invite a compiler to assume that a particular object wouldn't be changed in weird ways, such an assumption would be appropriate for the vast majority of objects in C programs, and it would have been impractical to add a qualifier to all the objects for which such assumption would be appropriate. On the other hand, some programs need to use some objects for which such an assumption would not hold. To resolve this issue, the Standard says that compilers may assume that objects which are not declared volatile will not have their value observed or changed in ways that are outside the compiler's control, or would be outside a reasonable compiler's understanding.
Because various platforms may have different ways in which objects could be observed or modified outside a compiler's control, it is appropriate that quality compilers for those platforms should differ in their exact handling of volatile semantics. Unfortunately, because the Standard failed to suggest that quality compilers intended for low-level programming on a platform should handle volatile in a way that will recognize any and all relevant effects of a particular read/write operation on that platform, many compilers fall short of doing so in ways that make it harder to process things like background I/O in a way which is efficient but can't be broken by compiler "optimizations".
volatile在C语言中实际上是为了不自动缓存变量的值而存在的。它会告诉编译器不要缓存这个变量的值。因此,每次遇到给定的volatile变量时,它都会生成代码从主存中获取它的值。之所以使用这种机制,是因为该值在任何时候都可以被操作系统或任何中断修改。所以使用volatile可以帮助我们每次都重新访问值。
我会提到另一个挥发物很重要的场景。
假设您为更快的I/O对文件进行内存映射,并且该文件可以在幕后更改(例如,该文件不在您的本地硬盘驱动器上,而是通过网络由另一台计算机提供)。
如果您通过指向非易失性对象的指针(在源代码级别)访问内存映射文件的数据,那么编译器生成的代码可以多次获取相同的数据,而您却不知道它。
如果该数据碰巧发生了变化,您的程序可能会使用两个或多个不同版本的数据,并进入不一致的状态。如果程序处理不受信任的文件或来自不受信任位置的文件,这不仅会导致程序的逻辑不正确行为,而且还会导致可利用的安全漏洞。
如果您关心安全性,这是一个需要考虑的重要场景。
正如这里许多人正确地建议的那样,volatile关键字的流行用途是跳过volatile变量的优化。
在阅读了volatile之后,我想到的最好的优点是——在longjmp的情况下防止回滚变量。非本地跳转。
这是什么意思?
它只是意味着在你进行堆栈展开后,最后一个值将被保留,以返回到前一个堆栈帧;通常是在一些错误的情况下。
因为它超出了这个问题的范围,所以我不打算在这里详细讨论setjmp/longjmp,但是值得一读;以及如何使用波动特征来保留最后的价值。
参见Andrei Alexandrescu的文章,“volatile——多线程程序员最好的朋友”
The volatile keyword was devised to prevent compiler optimizations that might render code incorrect in the presence of certain asynchronous events. For example, if you declare a primitive variable as volatile, the compiler is not permitted to cache it in a register -- a common optimization that would be disastrous if that variable were shared among multiple threads. So the general rule is, if you have variables of primitive type that must be shared among multiple threads, declare those variables volatile. But you can actually do a lot more with this keyword: you can use it to catch code that is not thread safe, and you can do so at compile time. This article shows how it is done; the solution involves a simple smart pointer that also makes it easy to serialize critical sections of code.
本文适用于C和c++。
参见Scott Meyers和Andrei Alexandrescu的文章“c++和双重检查锁定的危险”:
So when dealing with some memory locations (e.g. memory mapped ports or memory referenced by ISRs [ Interrupt Service Routines ] ), some optimizations must be suspended. volatile exists for specifying special treatment for such locations, specifically: (1) the content of a volatile variable is "unstable" (can change by means unknown to the compiler), (2) all writes to volatile data are "observable" so they must be executed religiously, and (3) all operations on volatile data are executed in the sequence in which they appear in the source code. The first two rules ensure proper reading and writing. The last one allows implementation of I/O protocols that mix input and output. This is informally what C and C++'s volatile guarantees.