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

时间:2015-02-22 04:06:38

标签: c language-lawyer volatile

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

  

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

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

int x;
volatile int *p = &x;
*p = 42;

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

由于它可能是有意义的,我感兴趣的特定用例不在普通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

2 个答案:

答案 0 :(得分:8)

更新 2017年2月18日

以下答案引用&讨论了标准中的语言,基本原理中的一些矛盾语言以及来自gnu.cc的一些评论的矛盾。有一个缺陷报告基本上有标准应该说的委员会协议(虽然仍然是开放的),并且意图一直是,并且实现总是反映出,它不是对象的波动性重要(根据标准),但是(左值)的波动性(根据基本原理)。 (感谢Olaf提及此DR。)

C11版本1.10的缺陷报告摘要日期:2016年4月DR 476 volatile semantics for lvalues 04/2016打开


否。因为访问的对象不易变。

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

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

另一方面,this post引用了国际标准的基本原理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每个实现都可以定义抽象语义和实际语义之间更严格的对应关系。

因此,仅仅出现挥发性左值并不能告诉你什么是“访问”。除了记录在案的实施行为之外,您无权谈论“*ptmp = *p的单一访问权”。

答案 1 :(得分:2)

不完全确定,但我认为关键是对象具有的类型与对象定义的类型之间的区别。

从C11(n1570)6.3.2.1 p1(脚注省略,emph.mine):

  

lvalue 是一个表达式(对象类型不是void),可能指定一个对象;如果左值在评估时未指定对象,则行为未定义。 当一个对象被称为具有特定类型时,该类型由用于指定对象的左值指定。 [...]

它是左值,它定义了对象对特定访问的类型。相反,*p不表示对象已定义 volatile。例如,同上。 6.7.3 p6(emph.mine)读取

  

[...]如果尝试通过使用具有非volatile限定类型的左值来引用具有volatile限定类型的对象已定义,则行为未定义。 133)

     

133)这适用于行为就像使用限定类型定义的对象,即使它们实际上从未被定义为程序中的对象(例如内存映射中的对象)输入/输出地址)。

如果意图是允许显示的代码被优化,那么问题中的引用可能会读取 具有 [用[volatile]限定类型定义的对象可能被修改[...]

标识符 *)的“定义”在6.7第5段中定义。

同上。 6.7.3 p7(什么构成对具有volatile限定类型的对象的访问是实现定义的。)为实现者提供了一些余地,但对我来说,意图似乎是侧面 - 修改由n表示的对象的效果应该被认为是符合要求的实现。

*) Afaik标准没有在任何地方定义“用(某种类型)定义的对象”所以我把它读作“一个对象,由一个用(某种类型)声明的标识符指定定义“。

相关问题