【问题标题】:Why is "volatileQualifiedExpr + volatileQualifiedExpr" not necessarily UB in C but in C++?为什么“volatileQualifiedExpr + volatileQualifiedExpr”不一定是 C 中的 UB,而是 C++ 中的 UB?
【发布时间】:2012-10-04 13:56:01
【问题描述】:

当我今天阅读 C 标准时,它谈到了副作用

访问 volatile 对象、修改对象、修改文件或调用函数 做任何这些操作都是副作用

C++ 标准说

访问由 volatile glvalue (3.10) 指定的对象、修改对象、调用库 I/O 函数或调用执行任何这些操作的函数都是副作用

因此,由于两者都禁止在同一个标​​量对象上发生未排序的副作用,C 允许以下行为,但 C++ 使其成为未定义行为

int a = 0;
volatile int *pa = &a;

int b = *pa + *pa;

我是否正确阅读了规范?如果是这样,差异的原因是什么?

【问题讨论】:

  • 我不认为存在差异。对于 C,对象的类型是读取对象的类型,而不是对象最初声明的类型。所以*pa 是一个左值,指定了一个volatile 限定对象。
  • @JensGustedt 我假设它指的是“有效类型”的概念。对于具有声明类型的对象,它被定义为声明的类型。所以对于访问“*pa”,它的有效类型在这里是“int”,除非我遗漏了什么。
  • “当我今天阅读 C 标准时”
  • 啊.. 修复 wiki 上关于可变波动率的 Comp-Sci 文章。因为仅仅阅读 C 标准会很奇怪,但是这样做来更新 wiki-comp-sci 文章更……正常? =P顺便说一句。好问题。
  • 对对对,这里的有效类型是int。有趣的是,如果您使用 volatile int *pa = calloc(sizeof *pa, 1);,则有效类型将是 volatile。

标签: c++ c volatile side-effects


【解决方案1】:

我不相信在这方面 C 和 C++ 之间存在有效的变化。尽管关于排序的措辞不同,但最终结果是相同的:两者都导致未定义的行为(尽管 C 似乎表明评估将成功但结果未定义)。

在 C99 中(抱歉,手边没有 C11)第 5.1.2.3.5 段指定:

——在序列点,volatile 对象是稳定的,因为之前的访问是 完整且后续访问尚未发生。

结合您对 5.1.2.3.2 的引用表明,pa 的值对于至少一次对 pa 的访问不会处于稳定状态。这是合乎逻辑的,因为允许编译器以任何顺序评估它们,一次或同时(如果可能的话)。然而,它实际上并没有定义 stable 的含义。

在 C++11 中,1.9.13 明确引用了未排序的操作。那么第 15 点表示对同一操作数的这种未排序的操作是未定义的。由于 未定义的行为 可能意味着任何事情都会发生,它可能比 C 的 unstable 行为强。但是,在这两种情况下,都不能保证您的表达结果。

【讨论】:

  • 对不起,我不明白这个答案。
  • 简而言之,C 和 C++ 之间关于对 volatile 的无序列访问的标准没有真正的区别。两者都不能保证计算的结果。
  • This answer 似乎有相关的 C11 措辞:(6.5p2)“如果标量对象的副作用相对于同一标量对象的不同副作用或值计算是无序的使用相同标量对象的值,行为是未定义的。”
  • 这是一个很好的更新。它现在形式化它在 C 中也是未定义的。所以现在两种语言的行为是相同的。
  • @edA-qamort-ora-y :我相信这两种语言的行为总是是相同的。 OP (Johannes) 提供了一些来自标准的引用,这些引用似乎基本相同:同时从两个方向访问 volatile 对象是个坏消息。 C11 采用了 C++11 的序列 ing 概念(与序列 points 相对),但在这种情况下它不会改变 re UB 的含义。
猜你喜欢
  • 2016-01-25
  • 1970-01-01
  • 2011-02-15
  • 1970-01-01
  • 2020-08-12
  • 1970-01-01
  • 1970-01-01
  • 2021-10-30
相关资源
最近更新 更多