序列点来自哪里?

时间:2012-06-25 17:47:31

标签: c++ c sequence-points

我知道写点像

++a = a++;

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

这些限制来自哪里?在将它们视为错误之前,如何才能看到这些“问题”?

4 个答案:

答案 0 :(得分:9)

基本上每个语句之间都有一个C ++ 03序列点。有关更多信息,请参阅SO C++ FAQ。有关更多信息,请参阅C ++标准,并记住在C ++ 11标准中,序列点被替换为之前排序,关系之后排序。

为避免出现问题,请不要试图在每个表达式中做太多聪明。

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

提示:尽可能洒掉const

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

答案 1 :(得分:8)

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

但是,如果您正在撰写“有趣”的代码,例如您的示例,那么您通常只会违反序列点要求。语言标准可以对诸如此类的代码(这是Java所使用的语言)施加特定约束,但是没有很大的好处,以及阻止某些类型的优化的潜在缺点。

<小时/> <子> 1。 C ++ 11中的术语略有变化,但我认为原理基本相同。

答案 2 :(得分:5)

  

如何在发现错误之前看到这些“问题”?

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

使用gcc,你可以使用:

-Wsequence-point

指出序列点问题。请注意,如果您使用-Wall,则会默认启用它。

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

答案 3 :(得分:-1)

  

这些限制来自哪里?在发现它们为错误之前,如何看待那些“问题”?

它们来自执行过程中操作顺序的含糊不清(或自由修改)。

例如:

++a = a++;

对于左值的预增是在右值的后增之前还是之后,该语言未定义。在此处添加约束将导致大量成本;一般好处是什么?该示例代码“应”如何清晰,明显地表现出来?

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

过度限制执行顺序:

  • 使针对不同目标体系结构的C / C ++更加困难
  • 增加语言规范的复杂性
  • 降低性能(作为回报?)
  • 防止许多编译时优化,降低性能
  • 要求在执行过程中禁用流水线和硬件重新排序,从而再次降低性能
  • 也许会破坏现有代码

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

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

假设只有有限数量的寄存器可用,并且某些变量(“ d”和“ e”)在寄存器中,而有些则没有。严格限制执行顺序(从左到右)可能需要:

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

例如,如果允许首先处理'd'和'e'并在稍晚的时间增加'b',编译器也许能够显着减少步骤数并提高性能。