9

我知道写类似的东西

++a = a++;

不仅不可读,而且违反了 c/c++ 序列点。

这些限制从何而来?在发现它们是错误之前,如何才能看到这些“问题”?

4

4 回答 4

9

基本上每个语句之间都有一个 C++03 序列点。有关详细信息,请参阅SO C++ 常见问题解答。有关更多信息,请查阅 C++ 标准,并记住在 C++11 标准中,序列点被替换为先排序后排序关系。

为了避免出现问题,不要试图在每个表达式中做很多事情。

不要试图做编译器的工作:把它留给编译器。你的工作是编写其他人容易理解的代码,即清晰的代码。多次更新和不必要地使用具有副作用的运算符与此不兼容。

提示:几乎在任何可能的地方洒const

这限制了读者必须考虑的可能的状态变化。

于 2012-06-25T17:55:16.330 回答
8

它们来自 C 或 C++ 标准,该标准有效地列出了序列点1 在一些简单的情况下,您的编译器可能会警告您正在调用未定义的行为,但在一般情况下不会。

但是,如果您正在编写诸如示例之类的“有趣”代码,则通常只会违反序列点要求。语言标准可能会对诸如此类的代码施加特定的限制(Java 等语言就是这样做的),但没有太大的好处,并且可能会阻止某些类型的优化。


1. 我认为 C++11 中的术语略有变化,但原理大致相同。

于 2012-06-25T17:49:48.170 回答
5

在发现它们是错误之前,如何才能看到这些“问题”?

以最严格的级别编译您的程序,并使所有警告的设置都被指出为错误。大多数主流编译器都会指出由于序列点导致的未定义行为错误。

使用 gcc,您可以使用:

-Wsequence-point

这将指出序列点问题。请注意,如果您使用-Wall.

当然,最好的方法是尝试编写更具可读性的代码,以避免序列点错误冒险

于 2012-06-25T17:49:26.687 回答
-1

这些限制从何而来?在发现它们是错误之前,如何才能看到这些“问题”?

它们来自执行期间操作顺序的模糊性(或自由修改)。

对于您的示例:

++a = a++;

该语言未定义左值的预增量是否应该发生在右值的后增量之前或之后。在此处添加约束会产生巨大的成本;一般的好处是什么?这个示例代码“应该”表现的清晰、明显的方式是什么?

为了提高性能,程序中的操作顺序可以由编译器和/或处理器更改(在严格的限制内)。

过度约束执行顺序会:

  • 为不同的目标架构实现 C/C++ 变得更加困难
  • 增加语言规范的复杂性
  • 削减性能(以换取什么收益?)
  • 防止许多编译时优化,降低性能
  • 需要在执行期间禁用流水线和硬件重新排序,再次降低性能
  • 可能会破坏现有代码

让我们看看限制不同代码示例的执行顺序:

a = b++ + ++c - --d - e--;

假设只有有限数量的寄存器可用,并且一些变量('d' 和 'e')在寄存器中,而另一些则不在。严格限制执行顺序(比如说从左到右)可能需要:

  • 从寄存器中丢弃“d”
  • 从寄存器中丢弃“e”
  • 加载“b”
  • 将其初始值保存为 'b,original'
  • 递增“b”(对于某些数据类型可能很重要)
  • 存储修改后的“b”
  • 重新加载'b,原始'
  • 加载“c”
  • 递增“c”
  • 保存更新的“c”
  • 添加 'b,original' + 'c' 并保存为部分结果
  • 等等等等

虽然如果允许首先处理“d”和“e”并稍晚增加“b”,编译器可能能够显着减少步骤数并提高性能。

于 2018-11-09T22:20:22.117 回答