当前位置: 首页 > 知识库问答 >
问题:

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

萧无尘
2023-03-14

C11 6.7.3类型限定符,第7段规定:

int x;
volatile int *p = &x;
*p = 42;
do {
    tmp = *p;
    new = f(tmp);
} while (atomic_cas(p, tmp, new) != success);

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

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

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

共有1个答案

郭子航
2023-03-14

2017年2月18日更新

下面的答案引用&讨论了标准中的语言,理论基础中的一些矛盾语言,以及GNU.CC关于矛盾的一些评论。有一份缺陷报告,它基本上得到了委员会的同意(尽管仍未公开),即标准应该说,目的一直是,实现一直反映,重要的不是对象的波动性(根据标准),而是访问的波动性(根据理论基础)。(归功于奥拉夫提到这位博士)

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

不会,因为访问的对象不是易失性的。

对象p的类型是指向volatile int的指针。但是x不是volatile限定类型的对象。p上的限定条件影响可以通过它进行的访问,但不影响它所指向的对象的类型。通过volatile lvalue访问非限定类型对象没有任何限制。因此通过p访问x不是访问volatile限定类型的对象。

(关于访问限定类型的对象的限制,请参见6.7.3类型限定符。它只是说你不能通过限定的lvalue访问易变的限定对象。)

另一方面,这篇文章引用了国际标准--编程语言--C:

将值强制转换为限定类型无效;这种限定(例如,不稳定的)可能不会对访问产生影响,因为它在案件发生之前就已经发生了。如果需要使用volatile语义访问非volatile对象,方法是将对象的地址强制转换为适当的指向限定类型的指针,然后取消对该指针的引用。

然而,我在标准中找不到语言说语义是基于lvalue类型的。来自gnu.org:

[..]从标准中并不清楚,如果底层对象是普通的,那么volatile lvalue在总体上是否比非volatile lvalue提供更多的保证。

不,因为没有副作用:

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

还要记住

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

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

所以仅仅是volatile lvalues的出现并不能告诉您有什么“访问”。您无权谈论“从tmp=*p*p的单次访问”,除非使用文档化的实现行为。

 类似资料:
  • 我正在使用一个API,该API为内存映射的I/O提供指针。它通过填写指针到指针的参数来实现这一点: 因为这是内存映射的I/O,所以我很确定我应该在这里使用。(我不知道为什么他们没有将参数设为。) 但是,我的volatile void**不能隐式转换为函数的,因此我不能直接传入它: 我目前正在使用一个额外的变量和一个单独的赋值步骤来解决这个问题: 这似乎很冗长。在这种情况下,当指针传入时,将vola

  • 来自C/C++,我对Java中的volatile对象行为有点困惑。 null faik,volatile意味着b引用的“book对象”应该在主内存中。编译器可能在内部实现引用作为指针,因此b指针可能位于缓存中。我的理解是,volatile是对象的限定符,而不是引用/指针的限定符。 问题是:在使用方法中,本地引用不是易失性的。这个“本地”引用会不会把底层的Book对象从主存带到缓存中,实质上使对象不

  • 我对C和C++中语义的理解是,它将内存访问变成了(可观察到的)副作用。每当读取或写入内存映射文件(或共享内存)时,我都希望指针是volatile限定的,以表明这实际上是I/O。(John Regehr写了一篇关于语义的很好的文章)。 此外,我认为使用这样的函数访问共享内存是不正确的,因为签名表明volatile限定已被放弃,并且内存访问不被视为I/O。 在我看来,这是一个支持的论点,在这里vola

  • 在阅读了这个问题和这个(尤其是第二个答案)之后,我对volatile及其关于记忆障碍的语义感到非常困惑。 在上面的例子中,我们写入一个易失性变量,这会导致一个mitch,这反过来会将所有挂起的存储缓冲区/加载缓冲区刷新到主缓存,使其他缓存行无效。 然而,非易失性字段可以优化并存储在寄存器中,例如?那么,我们如何才能确保给定一个写入易失性变量之前的所有状态变化都是可见的呢?如果我们有1000件东西呢

  • 来自ISO/IEC 9899:201x第5.1.2.3节程序执行第4段: 关于volatile对象,这里允许的优化是什么?有人能给出一个可以优化掉的易失性访问的例子吗? 由于Volatile访问是一种可观察的行为(在第6段中描述),似乎没有任何优化可以针对Volatile进行,所以,我很想知道第4节中允许的优化是什么。

  • 如果是,如果被添加到数组中,强制转换会受到什么影响?谢谢你! 编辑:@cacahuete Frito链接了一个非常相似的问题:带有'volatile'数组的'memcpy((void*)dest,src,n)'安全吗?