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


当前回答

我的简单解释是:

在某些情况下,基于逻辑或代码,编译器会对它认为不会改变的变量进行优化。volatile关键字阻止变量被优化。

例如:

bool usb_interface_flag = 0;
while(usb_interface_flag == 0)
{
    // execute logic for the scenario where the USB isn't connected 
}

从上面的代码中,编译器可能认为usb_interface_flag被定义为0,并且在while循环中它将永远为0。优化后,编译器会一直将其视为while(true),导致无限循环。

为了避免这种情况,我们将标志声明为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告诉编译器你的变量可以通过其他方式被改变,而不是通过访问它的代码。例如,它可能是一个I/ o映射的内存位置。如果在这种情况下没有指定这一点,一些变量访问可以被优化,例如,它的内容可以保存在寄存器中,并且内存位置不会再次读入。

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

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

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

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

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

简单来说,它告诉编译器不要对特定变量做任何优化。映射到设备寄存器的变量由设备间接修改。在这种情况下,必须使用volatile。

Volatile意味着存储可能在任何时候被改变,而且是在用户程序控制之外被改变。这意味着如果你引用变量,程序应该总是检查物理地址(即映射的输入fifo),而不是以缓存的方式使用它。