为什么C中需要挥发性?它的用途是什么?它会做什么?


当前回答

volatile的另一个用途是信号处理程序。如果你有这样的代码:

int quit = 0;
while (!quit)
{
    /* very small loop which is completely visible to the compiler */
}

编译器可以注意到循环体没有触及quit变量,并将循环转换为while (true)循环。即使在信号处理程序上为SIGINT和SIGTERM设置了退出变量;编译器无法知道这一点。

但是,如果quit变量被声明为volatile,编译器将被迫每次加载它,因为它可以在其他地方修改。这正是你在这种情况下想要的。

其他回答

volatile的另一个用途是信号处理程序。如果你有这样的代码:

int quit = 0;
while (!quit)
{
    /* very small loop which is completely visible to the compiler */
}

编译器可以注意到循环体没有触及quit变量,并将循环转换为while (true)循环。即使在信号处理程序上为SIGINT和SIGTERM设置了退出变量;编译器无法知道这一点。

但是,如果quit变量被声明为volatile,编译器将被迫每次加载它,因为它可以在其他地方修改。这正是你在这种情况下想要的。

维基上有关于volatile的所有信息:

Volatile(计算机编程)

Linux内核的文档也对volatile做了一个很好的标记:

为什么不应该使用“volatile”类型类

volatile在C语言中实际上是为了不自动缓存变量的值而存在的。它会告诉编译器不要缓存这个变量的值。因此,每次遇到给定的volatile变量时,它都会生成代码从主存中获取它的值。之所以使用这种机制,是因为该值在任何时候都可以被操作系统或任何中断修改。所以使用volatile可以帮助我们每次都重新访问值。

我会提到另一个挥发物很重要的场景。

假设您为更快的I/O对文件进行内存映射,并且该文件可以在幕后更改(例如,该文件不在您的本地硬盘驱动器上,而是通过网络由另一台计算机提供)。

如果您通过指向非易失性对象的指针(在源代码级别)访问内存映射文件的数据,那么编译器生成的代码可以多次获取相同的数据,而您却不知道它。

如果该数据碰巧发生了变化,您的程序可能会使用两个或多个不同版本的数据,并进入不一致的状态。如果程序处理不受信任的文件或来自不受信任位置的文件,这不仅会导致程序的逻辑不正确行为,而且还会导致可利用的安全漏洞。

如果您关心安全性,这是一个需要考虑的重要场景。

volatile变量可以从编译代码的外部进行更改(例如,程序可以将volatile变量映射到内存映射寄存器)。编译器不会对处理易失性变量的代码应用某些优化——例如,它不会在不将其写入内存的情况下将其加载到寄存器。这在处理硬件寄存器时很重要。