为什么C中需要volatile?
为什么C中需要volatile
? 它是干什么用的? 它会做什么?
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;
}
看起来很简单,但它可能会失败,因为编译器可以自由更改写入数据和命令的顺序。 这会导致我们的小工具使用之前的数据值发布命令。 还要看看等待忙碌的循环。 那一个将被优化。 编译器会尽量聪明,只读一次isbusy的值,然后进入无限循环。 这不是你想要的。
解决这个问题的方法是将指针小工具声明为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
是信号处理程序。 如果你有这样的代码:
quit = 0;
while (!quit)
{
/* very small loop which is completely visible to the compiler */
}
编译器可以注意到,循环体不会触及quit
变量,并将循环转换为while (true)
循环。 即使在SIGINT
和SIGTERM
的信号处理程序上设置了quit
变量, 编译器无法知道这一点。
但是,如果quit
变量被声明为volatile
,则编译器会被迫每次加载它,因为它可以在其他地方修改。 这正是你在这种情况下想要的。
C中的volatile
实际上是为了不自动缓存变量的值而存在的。 它会告诉机器不要缓存这个变量的值。 因此,每次遇到它时,都会从主内存中获取给定volatile
变量的值。 这种机制的使用是因为在任何时候该值都可以被操作系统或任何中断修改。 所以使用volatile
会帮助我们每次重新访问这个值。