C11 6.7.3类型限定词,第7段,内容如下:
具有volatile限定类型的对象可能以实现未知的方式进行修改,或者具有其他未知的副作用。因此,任何涉及这种对象的表达都应严格按照抽象机的规则进行评估,如5.1.2.3所述。
在以下示例中,第三行中访问的对象是否受上述规则约束?
int x;
volatile int *p = &x;
*p = 42;
换句话说,左值这个事实 *p
有类型 volatile int
表示正在访问易失性对象,或者是否正在访问它 p
指向非易失性对象 x
意味着编译器可以利用这些知识进行优化并省略易失性访问?
由于它可能是有意义的,我感兴趣的特定用例不在普通C的范围内;它涉及使用pre-C11结构(可以是内联asm或简称为黑盒子)进行线程同步的原子,用于原子比较和交换,具有以下习语:
do {
tmp = *p;
new = f(tmp);
} while (atomic_cas(p, tmp, new) != success);
这里是指针 p
会有类型 volatile int *
,但是我担心当实际指向的对象是非易失性时会发生什么,特别是编译器是否可以将单个访问转换为 *p
从 tmp = *p
进入以下形式的两次访问:
do {
new = f(*p);
} while (atomic_cas(p, *p, new) != success);
这显然会导致代码不正确。因此,目标是确定是否所有这些指向的对象实际上都需要 volatile int
。
更新 2017年2月18日
下面的答案引用并讨论了标准中的语言,基本原理中的一些矛盾语言和gnu.cc中的一些评论是矛盾。有一个缺陷报告基本上有标准应该说的委员会协议(虽然仍然是公开的),并且意图一直是,并且实施总是反映出,它不是波动性 一个东西 重要的(根据标准),但是波动性 (访问的左值) (根据理由)。 (归功于 奥拉夫 提到这个DR。)
C11版本1.10日期:2016年4月的缺陷报告摘要 DR 476左值的易失性语义 04/2016开放
不。因为访问的对象不易变。
目的 p
是指向volatile int的类型指针。但 x
不是volatile限定类型的对象。 p上的资格会影响通过它进行的访问,但不会影响它指向的对象的类型。通过volatile左值访问非限定类型对象没有限制。因此,通过p访问x不是对volatile属性类型的对象的访问。
(有关访问限定类型对象的限制,请参见6.7.3类型限定符。它只是说您无法通过不合格的左值访问volatile限定对象。)
另一方面, 这个帖子 引用国际标准理论的6.7.3 - 编程语言 - C:
将值转换为限定类型无效;资格
(volatile,比如说)因为它已经发生而对访问没有任何影响
案件之前。如果有必要访问非易失性对象
使用volatile语义,技术是转换地址
对象指向适当的指向限定类型,然后取消引用
那个指针。
但是,我无法在标准中找到语言,即语义基于左值类型。从 gnu.org:
混淆的一个方面是用于定义的对象之间的区别
volatile类型和volatile左值。从C标准来看
在视图中,使用volatile类型定义的对象具有外部可见性
行为。您可以将这些对象视为只有很少的示波器
探针附加到它们,以便用户可以观察一些属性
访问它们,就像用户可以观察写入的数据一样
输出文件。但是,该标准并未明确是否
用户可以观察易失性左值对普通对象的访问。
[...]从标准中不清楚是否挥发性左值
提供更多的保证,一般比非易失性左值,如果
基础对象是普通的。
不,因为没有副作用:
即使是语义 *p
必须是挥发性的,但标准仍然说:
5.1.2.3程序执行4在抽象机器中,所有表达式都是
按语义指定的方式进行评估。一个实际的实现
如果它可以推断出它的表达式,则无需评估表达式的一部分
不使用值,不产生所需的副作用
(包括通过调用函数或访问volatile而导致的任何问题
目的)。
同样,代码中没有volatile对象。虽然是一个只能看到的编译单元 p
无法进行优化。
还要记住
6.7.3类型限定符7 [...]对具有volatile限定类型的对象的访问构成是实现定义的。
5.1.2.3程序执行8每个实现都可以定义抽象语义和实际语义之间更严格的对应关系。
因此,仅仅出现挥发性左值不会告诉你有什么“访问”。你没有权利谈论“单一访问权限 *p
从 tmp = *p
“除了记录的实施行为。
不完全确定,但我认为重点是对象类型的区别 具有 和对象的类型 定义 用。
从C11(n1570)6.3.2.1 p1(脚注省略,emph.mine):
一个 左值 是一个表达式(对象类型不是 void
)可能指定一个对象;如果左值在评估时未指定对象,则行为未定义。 当一个对象被称为具有特定类型时,该类型由用于指定该对象的左值指定。 [...]
它是左值,它定义了对象对特定访问的类型。相反, *p
不表示对象 定义 易挥发。例如,同上。 6.7.3 p6(emph.mine)读
[...]如果试图引用某个对象 定义 通过使用具有非volatile限定类型的左值,使用volatile限定类型,行为是未定义的。133)
133) 这适用于那些行为就像使用限定类型定义的对象,即使它们实际上从未被定义为程序中的对象(例如内存映射输入/输出地址处的对象)。
如果意图是允许显示的代码被优化,问题中的引用可能会读取 一个对象 具有 [定义为]波动限定类型可能会被修改[...]
标识符的“定义”*) 在6.7第5段中定义。
同上。 6.7.3 p7(什么构成对具有volatile限定类型的对象的访问是实现定义的。给实施者一些余地,但对我来说,意图似乎是修改由表示的对象的副作用 n
应该被认为是符合要求的实施可观察到的。
*) Afaik标准没有在任何地方定义“用(某种类型)定义的对象”所以我把它读作“一个对象,由一个定义的(某种类型)声明的标识符指定”。