C ++中的volatile与可变


85

我对volatile和mutable之间的区别有疑问。我注意到两者都意味着可以对其进行更改。还有什么?它们是一样的吗?有什么不同?它们在哪里适用?为什么提出这两个想法?如何以不同方式使用它们?

非常感谢。

Answers:


112

mutable字段即使在通过访问的对象来改变const指针或引用,或在一个const对象,所以编译器知道不藏匿它R / O存储器。一个volatile位置是一个可以通过代码编译器不知道的(例如,一些内核级驱动程序)来改变,所以编译器不知道的无效假设值“不可能有下该值的优化如寄存器分配已更改”,因为它最后一次加载到该寄存器中。提供给编译器的信息截然不同,以停止截然不同的无效优化。


13
volatile也可以通过根本不涉及CPU的进程来更改对象。例如,通信外设中接收字节的寄存器可以在接收到字节后自身增加(这甚至可能触发中断)。另一个示例是外设中的挂起中断标志寄存器。
Mike DeSimone

55
而且,volatile不仅意味着对象可以在编译器不了解的情况下进行更改-这还意味着编译器无法消除对该对象的写入,即使这些写入似乎是无用的。例如:x = 1; x = 0; 如果x是易失性的,则编译器必须发出两个写操作(这在硬件级别可能很重要)。但是,对于非易失性对象,编译器可以选择不打扰编写,1因为它从未使用过。
Michael Burr 2010年

15
一个对象可以同时标记为constvolatile!您无法更改对象,但是可以在背后进行更改。
CTMacUser

2
@Destructor:通常情况是写入硬件设备寄存器。
迈克尔·伯

5
@Destructor假设您正在控制LED的状态。写0将其关闭,写1将其打开。如果我需要使LED闪烁以传达某些错误状态,但是编译器决定优化除最后一个写操作以外的所有写操作,因为没有使用任何值,则LED不会闪烁并且无法实现我想要的行为。
iheanyi

28

mutable:mutable关键字会覆盖所有封闭的const语句。const对象的可变成员可以被修改。

volatile注意:volatile关键字是依赖于实现的修饰符,在声明变量时使用,这可防止编译器优化这些变量。易失性应与变量的值可能以意想不到的方式(即通过中断)改变的变量一起使用,这可能与编译器可能执行的优化冲突。

资源


您说Volatile should be used with variables whose value can change in unexpected ways我们应该更喜欢随机使用它吗?
Asif Mushtaq

@AsifMushtaq没有值。方法。可变影响您编写的代码的权限。因此,您可以通过const ptr或const引用访问变量。如果您的代码没有更改怎么办?编译器无法检查ptr或引用类型吗?多数民众赞成在挥发。易失性还强制将缓存写回到主内存。因此,这是使用具有多线程代码的LOT。:)

22

他们绝对不是同一回事。可变与const交互。如果您有const指针,则通常无法更改成员。可变为该规则提供了一个例外。

另一方面,易失性与程序所做的更改完全无关。这意味着内存可能会由于编译器无法控制的原因而发生变化,因此编译器每次都必须读取或写入内存地址,并且无法将内容缓存在寄存器中。


“另一方面,易失性与程序所做的更改完全无关...” -hmmm,使成员易失,并查看编译过程中发生了什么中断。尝试在事实之后添加volatile非常类似于尝试在事实之后添加const ...痛苦。
jww 2015年

@jww:与程序编写的内容完全无关。您可以采用类型的对象的地址T,并将其存储到中const T*并从中读取。如果创建该对象volatileconst T*即使您从未尝试写入它,也无法将其地址存储到其中。 volatile程序代码的更改/修改/内存写入是完全正交的。
Ben Voigt 2015年

17

粗略而有效的差异思考方法是:

  • 编译器知道可变对象何时更改。
  • 编译器无法知道易失性对象何时更改。

1
volatile依此类推:bytes_received,mutablereference_count。
Mike DeSimone 2010年

11

标记mutable为变量的变量允许在声明的方法中对其进行修改const

标记为变量的变量volatile告诉编译器,每次您的代码也告诉变量时,它必须读/写变量(即,它不能优化对变量的访问)。


4

我想补充一点,在处理多线程应用程序时,volatile也非常有用,例如,您拥有主线程(main()所在的位置),并且生成了一个工作线程,该工作线程将在变量“ app_running”为true时保持旋转。main()控制“ app_running”是true还是false,因此,如果不将volatile属性添加到“ app_running”的声明中,则如果编译器在辅助线程main(( )可能会将“ app_running”更改为false,但辅助线程将继续运行,因为该值已被缓存。我已经在Linux和VisualC ++上使用gcc看到了相同的行为。放入“ app_running”声明中的“ volatile”属性解决了该问题。所以,


1
没有!这是一个普遍的误解。为此,C ++ 11和C11引入了原子stackoverflow.com/questions/8819095/…–
KristianR
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.