标准在哪里定义了可变变量可以更改?

时间:2018-07-23 12:19:28

标签: c++ language-lawyer c++17 volatile

标准在哪里定义volatile变量可以更改 未被发现?

我发现了两个关于volatile的规范性文本:

intro.execution/7

  

读取由 volatile glvalue ([basic.lval])指定的对象,修改对象,调用库I / O函数或调用执行任何这些操作的函数都是副作用,即执行环境状态的变化。对表达式(或子表达式)的评估通常包括值计算(包括确定对象的身份以进行glvalue评估和获取先前分配给对象的值以进行prvalue评估)和副作用的初始化。 >。当返回对库I / O函数的调用或评估通过易失性glvalue进行的访问时,即使该调用或volatile访问所隐含的某些外部操作(例如I / O本身)也被认为是完全的副作用。可能尚未完成。

本段是否涉及未发现的更改? 副作用可能意味着这个吗?


或者有dcl.type.cv/5

  

通过易失性glvalue进行访问的语义是实现定义的。如果尝试通过使用非易失性glvalue来访问用volatile限定类型定义的对象,则行为是不确定的。

此段是关于我的问题吗? “通过易失性glvalue进行访问的语义是实现定义的”到底是什么意思?您可以举一个不同的“访问语义”的例子吗?


还有dcl.type.cv/6,这是关于我的问题,但这只是一个注释:

  

[注:volatile是实现的一种暗示,以避免涉及对象的积极优化,因为对象的值可能通过实现无法检测到的方式进行更改。此外,对于某些实现,volatile可能指示需要特殊的硬件指令才能访问该对象。有关详细的语义,请参见[intro.execution]。通常,volatile的语义在C ++中的意图与在C语言中是相同的。— —“ end note]

2 个答案:

答案 0 :(得分:7)

这里的关键是“执行环境状态的改变”。

执行环境是程序外部的内容。这可能包括操作系统,文件系统,屏幕等。通常是不可预测的。您不能假设如果向文件写入0,则该文件不会被另一个带有1的进程覆盖。

volatile变量在逻辑上是该执行环境的一部分。就C ++而言,环境可以像枚举文件一样枚举,读取和写入它们。这可能在您的程序不知道的情况下发生。

另一方面,您的实现实现了程序与其执行环境之间的链接,因此它确实对可能发生的事情有所了解。如果它具有某种专用的RAM磁盘实现,则它可能知道某些文件名在OS文件系统中不可见。并且它可能知道volatile int i位于CPU寄存器中,因此无法通过内存映射对其进行访问。这是C ++标准所允许的。它只是一般性地谈论执行环境,实现必须更加精确。这就是“实现定义的语义”的含义。

答案 1 :(得分:1)

volatile只是对编译器的请求,要求编译器为每次访问从内存中重新加载变量。它适用于2个常见用例:

  • 可以通过不同的线程(甚至可以授予该存储区写访问权限的其他程序)或内核模式代码(例如,通过特殊的驱动程序)更改变量。
  • 此变量表示一个物理内存寄存器,主要用于内核模式编程或在没有用户/内核模式概念的OS上使用,如老式MS / DOS。

一旦您知道,该标准中不同的引号都是有意义的。

  

读取由易失性glvalue([basic.lval])指定的对象,...都是副作用,它们是执行环境状态的变化。

读取硬件寄存器可能会对底层系统产生影响,这就是为什么它被认为是可观察到的副作用的原因。

  

通过易失性glvalue进行访问的语义是实现定义的。如果尝试通过使用非易失性glvalue来访问用volatile限定类型定义的对象,则行为是不确定的。

如果使用非易失性指针访问易失性硬件寄存器,则编译器可以缓存先前的值,而不执行物理访问。

  

[注:volatile是实现的一种暗示,以避免涉及对象的积极优化,因为对象的值可能通过实现无法检测到的方式进行更改。此外,对于某些实现,volatile可能指示需要特殊的硬件指令才能访问该对象。有关详细的语义,请参见[intro.execution]。通常,volatile的语义在C ++中的意图与在C语言中是相同的。— —“ end note]

某些实现可以为特殊的低级io端口操作保留一个存储区。在这种情况下,可能需要使用volatile说明符和该特殊存储区地址的组合才能通过特殊的io操作验证转换或正常的内存访问操作。