序列点从何而来
Where do sequence points come from?
我知道写类似的东西
++a = a++;
不仅不可读,而且违反了 c/c++ 序列点。
这些限制从何而来?在发现这些"问题"作为错误之前,如何才能看到它们?
基本上每个语句之间都有一个 C++03 序列点。有关更多信息,请参阅 SO C++常见问题解答。有关更多信息,请查阅C++标准,并记住,在 C++11 中,标准序列点被替换为之前排序和之后序列关系。
为了避免出现问题,不要试图在每个表达式中做很多事情时太聪明。
不要试图完成编译器的工作:把它留给编译器。你的工作是编写其他人可以轻松理解的代码,即清晰的代码。多次更新和不必要地使用带有副作用的运算符与此不兼容。
提示:几乎在可能的地方撒const
。
这限制了读者必须考虑的可能状态更改。
它们来自 C 或 C++ 标准,该标准有效地列出了序列点。1 在一些简单的情况下,编译器可能能够警告您正在调用未定义的行为,但在一般情况下则不然。
但是,通常只有在编写"有趣"代码(例如示例)时,才会违反序列点要求。 语言标准可能会对这样的代码施加特定的约束(这是Java等语言所做的),但没有太多的好处,以及阻止某些类型的优化的潜在缺点。
1. 术语在 C++11 中略有变化,但我认为原理仍然大致相同。
以在发现这些"问题"作为错误之前,如何才能看到它们?
最严格的级别编译程序,并启用所有警告的设置,以指出为错误。大多数主流编译器确实指出了由于序列点而导致的未定义行为错误。
使用 gcc,您可以使用:
-Wsequence-point
应指出序列点问题。请注意,如果您使用 -Wall
,则默认情况下会启用它。
当然,最好的方法是尝试编写更具可读性的代码,以避免序列点错误。
这些限制从何而来?在发现这些"问题"作为错误之前,如何才能看到它们?
它们来自执行过程中操作顺序的模糊性(或修改自由)。
对于您的示例:
++a = a++;
该语言未定义左值上的前递增是应该在右值上的后递增之前还是之后发生。 在此处添加约束将产生大量成本;一般好处是什么? 此示例代码"应该"的行为方式是什么?
为了提高性能,编译器和/或处理器可以更改程序中的操作顺序(在严格的限制内)。
过度约束执行顺序将:
- 使针对不同目标体系结构实施 C/C++变得更加困难
- 增加语言规范的复杂性
- 削减性能(换取什么收益?
- 防止许多编译时优化,降低性能
- 需要在执行期间禁用流水线和硬件重新排序,再次降低性能
- 也许破坏现有代码
让我们看一下约束不同代码示例的执行顺序:
a = b++ + ++c - --d - e--;
假设只有有限数量的寄存器可用,并且一些变量('d'和'e')在寄存器中,而另一些则不在寄存器中。 严格约束执行顺序(假设从左到右)可能需要:
- 从寄存器中丢弃"d"
- 从寄存器中丢弃"e"
- 加载"b"
- 将其初始值保存为"b,原始"
- 增量"b"(对于某些数据类型可能不平凡)
- 存储修改后的"b"
- 重新加载"b,原始"
- 加载"c"
- 增量"c"
- 保存更新的"c"
- 添加"b,原始"+"c"并另存为部分结果
- 等等。
如果允许例如先处理"d"和"e",并在稍晚的时间递增"b",编译器可能能够显着减少步骤数并提高性能。
- 没有找到相关文章