首页 文章

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

提问于
浏览
11

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

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

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

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

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

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

do {
    tmp = *p;
    new = f(tmp);
} while (atomic_cas(p, tmp, new) != success);

这里指针 p 的类型为 volatile int * ,但我担心当实际指向的对象是非易失性时会发生什么,特别是编译器是否可以将对 *p 的单一访问转换为以下形式的两次访问:

do {
    new = f(*p);
} while (atomic_cas(p, *p, new) != success);

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

2 回答

  • 2

    Update 2017年2月18日

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

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


    No. Because the object accessed is not volatile.

    对象 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 lvalues之间的区别 . 从C标准的角度来看,使用volatile类型定义的对象具有外部可见行为 . 您可以将这些对象视为附加了很少的示波器探针,以便用户可以观察到访问它们的一些属性,就像用户可以观察写入输出文件的数据一样 . 但是,该标准并未明确用户是否可以观察易失性左值对普通对象的访问 . [...]从标准中不清楚,如果基础对象是普通的,则挥发性左值一般比非易失性左值提供更多的保证 .

    No, because there are no side effects:

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

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

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

    Also keep in mind

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

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

    因此,仅仅出现挥发性左值不能告诉你"accesses"是什么 . 除了记录的实现行为之外,您无权谈论“从 tmp = *p 单一访问 *p ” .

  • 7

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

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

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

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

    [...]如果尝试通过使用具有非volatile限定类型的左值来引用使用volatile限定类型定义的对象,则行为是未定义的.133)133)这适用于那些对象表现就好像它们是用限定类型定义的,即使它们实际上从未被定义为程序中的对象(例如内存映射输入/输出地址处的对象) .

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

    "Definition"的标识符*)在6.7第5段中定义 .

    同上 . 6.7.3 p7(对具有volatile限定类型的对象的访问权限是实现定义的 . )为实现者提供了一些余地,但对我来说,意图似乎是修改由对象表示的对象的副作用 n 应被视为符合要求的实施可观察到 .

    *)Afaik标准没有在任何地方定义"an object defined with (some type)"所以我把它读作"an object, designated by an identifier declared with (some type) by a definition" .

相关问题