C语言中的'volatile'关键字

31
我读了一些关于 C 语言中 volatile 的教程,但我仍然无法理解。有人说 volatile 告诉编译器优化器不应以某些方式优化涉及此变量的操作。这意味着每当寄存器中变量的值被更改时,该值都应影响内存。

另外,也有人说,volatile 意味着该值可以通过代码之外的手段进行更改。

我无法理解第二种说法。那么 volatile 变量可以由代码之外的手段更改吗?如何进行更改?这两个说法都正确吗?

6个回答

43

语句“该变量可以被此代码之外的方式更改”基本上意味着另一个程序或硬件可以更新该变量。这是完全可能的。一个思考方式是将这个概念与多个程序共享的文件相关联。一个文件可以同时被多个程序打开、写入和读取。当您从文件中读取时,要确保读取的是最新的更新,而不是最老的。

回到“volatile”关键字,将“volatile”放在变量前面实际上是做同样的事情。它确保您从变量中读取的内容不是基于编译器的优化或程序拥有的旧副本。此外,“volatile”关键字确保每次访问时变量都从内存中获取。因此,这两个语句都正确描述了“volatile”关键字。


1
我不明白硬件如何改变变量?这是否意味着硬件不需要一些方法或代码来修改变量? - user707549
9
@ratzip:是的。变量是存储值的内存位置,而内存只是一种硬件(例如RAM芯片)。许多计算机系统(键盘、网络、音频输入等)被设置成可以使外部硬件直接修改某些内存位置。 - e.James

21

C语言不仅仅用于计算机。例如,如果你在开发Game Boy Advance游戏,你经常会遇到硬件修改的内存位置,所以 你可能不会在代码中修改变量 ,但它仍然被修改了。这就是 volatile 的意义。

通过添加 volatile 关键字,你告诉编译器 “这个变量(内存位置)存储的值可能会在没有我的代码干扰下发生改变。”


2
@随机: 因为事情就是这样。无论如何,几乎每个平台都需要与硬件交互。 - Oliver Charlesworth
在某些嵌入式平台上,例如GBA,您具有内存映射寄存器,因此您确切知道内存中的某个特定区域将会以某种方式运行。 - aviraldg
@Random:那不是正确的。一个多线程应用程序可以轻松地生成两个线程异步访问同一内存位置的代码。 - e.James
@Andrew Lazarus,在可移植的C程序中不可能实现这一点 - 标准没有定义任何方法来实现。@e.James并且C标准不提供多线程,那么为什么应该提供只有在使用扩展时才必要/有用的关键字呢? - Random832
5
@Random:C标准并没有定义如何解析#include,但这并不意味着它们没有用处。 - Oliver Charlesworth
显示剩余3条评论

15

考虑以下任何一种情况:

  • 多线程应用程序,
  • 使用共享内存的应用程序,
  • 在将I/O寄存器映射到地址空间的平台上运行的应用程序,
  • 存在后台硬件DMA的应用程序。

在这些情况中,内存可能会在当前线程之外被更改。

请注意,“每当寄存器中变量的值发生更改时,则该值应影响内存”是正确的,只是不太清晰。


这句话的翻译是:“这两种说法都正确吗?” - user707549
3
但是在多线程代码中,volatile 几乎没有什么用处。 - curiousguy
2
volatile 在数据可能被异步事件修改的情况下也很有用,例如信号处理程序代码的执行(即信号处理程序代码修改了变量)。 - vyudh
4
在C语言规范中,对于volatile关键字没有要求其具有获取/释放语义或其他实际用于多线程代码的语义。请记住,不同的线程可能在不同的处理器上操作不一致的缓存;编译器实现volatile时没有要求考虑这一点。除非你正在为特定的编译器编写代码,并且你知道编译器相对于volatile实现了哪些C语言扩展,否则不应该使用volatile来实现线程语义。 - Eric Lippert
@EricLippert: C标准中也没有规定符合标准的实现必须适合任何目的。标准不要求符合标准的实现做任何事情,并不意味着旨在适用于低级编程的高质量实现不应该这样做。不幸的是,编译器将质量实现问题视为实现低质量语义的借口,这已成为一种时尚。 - supercat

6

一个内存位置可以通过多种方式在程序代码之外被改变。例如,从磁盘进行的DMA读取可以写入缓冲区,或者由于设备上的某些事件而导致内存映射设备更改位置。


3

例如,这涉及到多线程应用程序:一个变量的值可能会被多个线程改变,因此每次访问(无论是读取还是写入该值)都必须与内存“同步”。


3
在多线程代码中,volatile 非常非常少用。 - curiousguy

2

声明一个volatile变量意味着您正在指示编译器不要优化该变量周围的代码片段。这是为了强制CPU不使用从本地寄存器或缓存内存中获取变量值,而是每次都从主内存中获取其值。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接