【问题标题】:Is "++l *= m" undefined behaviour?“++l *= m”是未定义的行为吗?
【发布时间】:2011-05-19 05:19:54
【问题描述】:

我已经开始学习 C++0x。我在某处遇到了以下表达式:

int l = 1, m=2;
++l *= m;

我不知道第二个表达式是否有明确定义的行为。所以我在这里问。

不是UB吗?我只是想知道。

【问题讨论】:

  • 问题是在 c++0x 的上下文中。
  • ++l 的结果是非 const 引用?为什么 C++ 语言设计者会这样定义它?
  • 说真的,为什么人们首先要编写这样的代码???
  • 我第二次 Freds 的声明,如果你非要问,不要问。
  • 如果您尝试理解其他人编写的代码,这是一个合理的问题。不过,千万不要自己编写这种代码。

标签: c++ c++11


【解决方案1】:

表达式在 C++0x 中定义良好。 Prasoon here 给出了一个非常标准的引用常见问题解答。

我不相信 (字面标准引号:解释性文本) 的比例如此之高是可取的,所以我给出一个额外的小解释:记住 ++L 相当于L += 1,并且该表达式的值计算在L 的增量之后排序。而在a *= b 中,表达式a 的值计算在将乘法结果赋值给a 之前排序

你有什么副作用?

  • 增量
  • 乘法结果的赋值

这两个副作用都是由上述两个sequenced aftersequenced before传递排序的。

【讨论】:

  • 没有。 IIUC 的标准 (L += 1) 的副作用是在值计算之后启动,但你不能假设它会在另一个副作用之前完成(*= ) 启动。编译器生成的代码可以等到完整表达式的最后才真正完成L的更新。
  • @6502 我已经在几周前从标准草案中阅读了所有这些内容,并且已经在其他地方对 SO 进行了详细的辩论。我的解释和@Prasoon 的标准引用应该让您开始使用 n3126 并阅读它并验证它的有效性。 L += 1 的副作用是通过计算该表达式来启动的,并且在 L 的值计算 之后 和整个表达式的 之前 值计算之后进行排序。前序/后序是传递性的,所以如果A先序于B而B先序于C,则A先序于C。
  • 我不会再在这里讨论这个问题了。
  • @Johannes:好的。我投票赞成删除我的错误回复。很久以来我都没有深入研究 C++ 的内部结构,回想起来我认为这是一个好主意。 IMO 地球上 C++ 需要的最后一件事是增加复杂性和不对称性......但显然这正是发生的事情。尖锐的讨论说删除一些UB很好……但我不同意;这是通过特殊的大小写和增加 UB 分类的复杂性获得的,因此更多的程序员会在这些领域犯错误。 i = ++i + 1 会起作用,但这只是巧合,而不是程序员的深思熟虑。
  • 在我最初的回答中查看我与@6502 的讨论。我无法理解l 的左值计算实际上是按照 5.17(1) 进行排序的。与 5.3.2.1 和 5.17(7) 的关联启发了我。谢谢你的耐心。 :)
【解决方案2】:

在上面的代码中,前缀++ 优先于*=,因此首先被执行。结果是l 等于4

更新:这确实是未定义的行为。我认为优先规则是错误的。

原因是l*=++ 中都是左值和右值。这两个操作没有先后顺序。因此l 被写入(和读取)两次“没有序列点”(旧标准措辞),并且行为未定义。

作为旁注,我认为您的问题源于 C++0x 中序列点的变化。 C++0x 已将“序列点”的措辞更改为“之前排序”,以使标准更清晰。据我所知,这不会改变 C++ 的行为。

更新 2: 事实证明,根据N3126 draft for C++0x 的第 5.17(1)、5.17(7) 和 5.3.2(1) 节,实际上有一个明确定义的排序。 @Johannes Schaub 的回答是正确的,并记录了语句的顺序。功劳当然应该归功于他的回答。

【讨论】:

  • 不,据我了解这是正确的。请参阅我对您的回答的评论。
  • 这不是未定义的行为......但解释了为什么不适合这个空间。显然,这样的声明的有效性不是故意的,只是解决语言的另一个问题的副作用......尽管如此,大师们认为它对 C++ 有好处,所以它将保持有效。 IMO 的最终效果是人们会在这里读到这是有效的,并将在 C++ 程序中编写那种代码和显然是无辜的变体(然而是 UB)。有多棘手?例如i = i++ + 1 是UB,而i = ++i + 1 是有效的。我不是在开玩笑。
  • @6502 能否请您引用一个引文来启发我们?
  • 问题涉及到++L,相当于(L+=1)(见5.3.2(1))。 L+=1 是一个复合赋值(见 5.17(1))。相关部分是“在所有情况下,赋值顺序在左右操作数的值计算之后,在赋值表达式的值计算之前。”。现在*= 需要赋值的值(这是另一个复合赋值),并在赋值之前排序。不存在对 L 的多个未排序的副作用,因此代码是有效的。是的,我同意这很糟糕。
猜你喜欢
  • 2020-06-15
  • 2011-05-19
  • 1970-01-01
  • 2011-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多