为什么C中需要挥发性?它的用途是什么?它会做什么?
当前回答
在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可以帮助我们每次都重新访问值。
volatile变量可以从编译代码的外部进行更改(例如,程序可以将volatile变量映射到内存映射寄存器)。编译器不会对处理易失性变量的代码应用某些优化——例如,它不会在不将其写入内存的情况下将其加载到寄存器。这在处理硬件寄存器时很重要。
它不允许编译器自动改变变量的值。易失性变量用于动态使用。
volatile的边缘用法如下。假设你想计算一个函数f的数值导数:
double der_f(double x)
{
static const double h = 1e-3;
return (f(x + h) - f(x)) / h;
}
问题是由于舍入误差,x+h-x通常不等于h。想想看:当你减去非常接近的数字时,你会丢失很多有效的数字,这可能会破坏导数的计算(想想1.00001 - 1)
double der_f2(double x)
{
static const double h = 1e-3;
double hh = x + h - x;
return (f(x + hh) - f(x)) / hh;
}
但是根据您的平台和编译器开关的不同,该函数的第二行可能会被积极优化的编译器删除。所以你可以写
volatile double hh = x + h;
hh -= x;
强制编译器读取包含hh的内存位置,从而丧失最终的优化机会。
正如这里许多人正确地建议的那样,volatile关键字的流行用途是跳过volatile变量的优化。
在阅读了volatile之后,我想到的最好的优点是——在longjmp的情况下防止回滚变量。非本地跳转。
这是什么意思?
它只是意味着在你进行堆栈展开后,最后一个值将被保留,以返回到前一个堆栈帧;通常是在一些错误的情况下。
因为它超出了这个问题的范围,所以我不打算在这里详细讨论setjmp/longjmp,但是值得一读;以及如何使用波动特征来保留最后的价值。