指针指向非易失性对象的指针行为的要求

R..*_*R.. 11 c volatile language-lawyer

C11 6.7.3类型限定词,第7段,内容如下:

具有volatile限定类型的对象可能以实现未知的方式进行修改,或者具有其他未知的副作用.因此,任何涉及这种对象的表达都应严格按照抽象机的规则进行评估,如5.1.2.3所述.

在以下示例中,第三行中访问的对象是否受上述规则约束?

int x;
volatile int *p = &x;
*p = 42;
Run Code Online (Sandbox Code Playgroud)

换句话说,左值*p是否具有类型volatile int意味着正在访问易失性对象,或者p指向非易失性对象的事实是否x意味着编译器可以利用这些知识进行优化并省略易失性访问?

由于它可能是有意义的,我感兴趣的特定用例不在普通C的范围内; 它涉及使用pre-C11结构(可以是内联asm或简称为黑盒子)进行线程同步的原子,用于原子比较和交换,具有以下习语:

do {
    tmp = *p;
    new = f(tmp);
} while (atomic_cas(p, tmp, new) != success);
Run Code Online (Sandbox Code Playgroud)

在这里,指针p就会有型volatile int *,但我很担心会发生什么,当实际指向的对象是非易失性的,特别是是否编译器可以单址变换,以*ptmp = *p为以下形式的两次存取:

do {
    new = f(*p);
} while (atomic_cas(p, *p, new) != success);
Run Code Online (Sandbox Code Playgroud)

这显然会使代码不正确.因此,目标是确定是否所有这些指向的对象实际上都需要volatile int.

phi*_*pxy 8

2017年2月18日更新

下面的答案引用并讨论了标准中的语言,基本原理中的一些矛盾语言和gnu.cc中的一些评论是矛盾.有一个缺陷报告基本上有标准应该说的委员会协议(虽然仍然是公开的),并且意图一直是,并且实施总是反映出,重要的是不是对象的波动性(根据标准)但是(访问的左值)的波动性(根据基本原理).(感谢奥拉夫提到这个DR.)

C11版本1.10的缺陷报告摘要日期:2016年4月DR 476左值的易失性语义 04/2016打开


不会.因为访问的对象不是易变的.

Object p是指向volatile int的类型指针.但x它不是波动限定类型的对象.p的资格会影响通过它进行的访问,但不影响它指向的对象的类型.通过volatile左值访问非限定类型对象没有限制.因此,通过p访问x不是对volatile属性类型的对象的访问.

(有关访问限定类型对象的限制,请参见6.7.3类型限定符.它只是说您无法通过不合格的左值访问易失性限定对象.)

另一方面,这篇文章引用了国际标准的基本原理6.7.3 - 编程语言 - C:

将值转换为限定类型无效; 资格(volatile,比如说)可能对访问没有影响,因为它已经发生在案例之前.如果需要使用volatile语义访问非易失性对象,则该技术是将对象的地址强制转换为适当的指向限定类型​​的类型,然后取消引用该指针.

但是,我无法在标准中找到语言,即语义基于左值类型.来自gnu.org:

混淆的一个方面是使用volatile类型定义的对象与volatile lvalues之间的区别.从C标准的角度来看,使用volatile类型定义的对象具有外部可见行为.您可以将这些对象视为附加了很少的示波器探针,以便用户可以观察到访问它们的一些属性,就像用户可以观察写入输出文件的数据一样.但是,该标准并未明确用户是否可以观察易失性左值对普通对象的访问.

[...]从标准中不清楚,如果基础对象是普通的,则挥发性左值一般比非易失性左值提供更多的保证.

不,因为没有副作用:

即使语义*p必须是volatile 的语义,标准仍然说:

5.1.2.3程序执行4在抽象机器中,所有表达式都按语义指定的方式进行计算.实际实现不需要评估表达式的一部分,如果它可以推断出它的值未被使用并且不产生所需的副作用(包括由调用函数或访问易失性对象引起的任何副作用).

同样,代码中没有volatile对象.虽然只看到的编译单元p无法进行优化.

还要记住

6.7.3类型限定符7 [...]对具有volatile限定类型的对象的访问构成是实现定义的.

5.1.2.3程序执行8每个实现可以定义抽象语义和实际语义之间更严格的对应关系.

因此,仅仅出现挥发性左值并不能告诉你什么是"访问".你没有权利谈论"单一访问*p来自tmp = *p"除了每个文件执行的行为.

  • 基本原理的引用似乎表明意图不同于规范性文本...... :-( (2认同)
  • 即使一个对象是"普通的",我也建议有必要允许中断,其他线程等可能需要使用`volatile`语义来访问它,这受到非易失性访问可能的限制.以意想不到的方式表现.在某些现实情况下,代码需要在运行时分配缓冲区并使用volatile语义访问它们.如果从`malloc`返回的缓冲区被认为是`volatile`,则通过非易失性指针访问将调用UB,因此这样的缓冲区必须是"普通的".但是如果...的平常性... (2认同)
  • ...缓冲区意味着编译器可以忽略指向其内容的指针上的`volatile`限定符,在上下文之间共享数据所必需的语义是无法实现的. (2认同)