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


当前回答

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

其他回答

它不允许编译器自动改变变量的值。易失性变量用于动态使用。

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

Volatile告诉编译器不要优化与Volatile变量有关的任何东西。

至少有三个常见的原因使用它,所有的情况下,变量的值可以改变,而不需要从可见代码的操作:

当您与改变值本身的硬件进行交互时 当另一个线程运行时也使用了该变量 当有一个可能改变变量值的信号处理程序时。

假设你有一小块硬件被映射到RAM的某个地方,它有两个地址:一个命令端口和一个数据端口:

typedef struct
{
  int command;
  int data;
  int isBusy;
} MyHardwareGadget;

现在你想要发送一些命令:

void SendCommand (MyHardwareGadget * gadget, int command, int data)
{
  // wait while the gadget is busy:
  while (gadget->isbusy)
  {
    // do nothing here.
  }
  // set data first:
  gadget->data    = data;
  // writing the command starts the action:
  gadget->command = command;
}

看起来很简单,但可能会失败,因为编译器可以随意更改数据和命令的写入顺序。这将导致我们的小工具使用之前的数据值发出命令。还可以看看busy循环中的wait。这个会被优化掉。编译器会尽量聪明,只读取一次isBusy的值,然后进入一个无限循环。这不是你想要的。

解决这个问题的方法是将指针gadget声明为volatile。这样编译器就会被强制执行你所写的内容。它不能删除内存赋值,不能在寄存器中缓存变量,也不能改变赋值的顺序

这是正确的版本:

void SendCommand (volatile MyHardwareGadget * gadget, int command, int data)
{
  // wait while the gadget is busy:
  while (gadget->isBusy)
  {
    // do nothing here.
  }
  // set data first:
  gadget->data    = data;
  // writing the command starts the action:
  gadget->command = command;
}

它有两个用途。这些在嵌入式开发中特别常用。

编译器不会优化使用volatile关键字定义的变量的函数 Volatile用于访问RAM、ROM等中的精确内存位置。这通常用于控制内存映射设备,访问CPU寄存器和定位特定的内存位置。

参见程序集清单示例。 回复:在嵌入式开发中使用C“volatile”关键字

参见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.