8

最近,我看到很多关于输出的问题被问到一些疯狂但在语法上允许的代码语句,比如 likei = ++i + 1i=(i,i++,i)+1;。老实说,在实际编程中几乎没有人写过这样的代码。坦率地说,在我的专业经验中,我从未遇到过任何这样的代码。所以我通常最终会在 SO 上跳过这些问题。但最近,被问到的此类 Q 的数量之多让我想,如果我跳过此类 Q,是否错过了一些重要的理论。我认为这样的 Q 围绕着Sequence points. 坦率地说,我几乎对序列点一无所知,我只是想知道不知道它是否在某种程度上是一个障碍。那么有人可以解释一下理论/概念吗Sequence points,或如果可能,请指向解释该概念的资源。此外,是否值得花时间了解这个概念/理论?

4

5 回答 5

5

我能想到的最简单的答案是:

C++ 是根据抽象机器定义的。在抽象机器上执行的程序的输出仅根据执行“副作用”的顺序来定义。副作用被定义为对 IO 库函数的调用,以及对标记为 volatile 的变量的更改。

C++ 编译器可以在内部做任何他们想做的事情来优化代码,但是他们不能改变写入 volatile 变量和 io 调用的顺序。

序列点定义了 c/c++ 程序的心跳 - 序列点之前的副作用是“完整的”,而序列点之后的副作用尚未发生。但是,副作用(或者,可以间接影响副作用的代码(序列点内)可以重新排序。

这就是为什么理解它们很重要。如果没有这种理解,你对什么是 c++ 程序(以及它如何被激进的编译器优化)的基本理解是有缺陷的。

于 2010-11-05T10:34:53.873 回答
2

请参阅http://en.wikipedia.org/wiki/Sequence_point

这是一个非常简单的概念,所以你不需要投入太多时间 :)

于 2010-11-05T10:30:20.320 回答
2

是的,序列点的确切技术细节可能会让人毛骨悚然。但是遵循这些指导方针可以解决几乎所有的实际问题:

  • 如果表达式修改了一个值,那么在修改和该值的任何其他使用之间必须有一个序列点。
  • 如果您不确定一个值的两次使用是否由序列点分隔,请将您的代码分解为更多语句。

=这里的“修改”包括对,等中左侧值的赋值操作+=,以及++x, x++, --x, 和x--语法。(通常是这些递增/递减表达式,有些人试图变得聪明并最终陷入困境。)

幸运的是,大多数“预期”的地方都有序列点:

  • 在每个声明或声明的末尾。
  • 在每个函数调用的开始和结束时。
  • 在内置&&||运算符。
  • ?三元表达式中。
  • 在内置,逗号运算符处。(最常见于 for 条件,例如for (a=0, b=0; a<m && b<n; ++a, ++b)。)分隔函数参数的逗号不是逗号运算符,也不是序列点。

重载operator&&,operator||operator,不会导致序列点。这一事实带来的潜在惊喜是通常不鼓励超载它们的原因之一。

于 2010-12-28T23:00:11.057 回答
1

值得知道存在序列点,因为如果您不了解它们,您可以轻松编写在测试中运行良好但实际上未定义的代码,并且当您在另一台计算机上运行它或使用不同的编译选项时可能会失败。特别是如果您将例如编写为x++更大的表达式的一部分,其中还包含x您很容易遇到问题。

我认为没有必要完全学习所有规则-但您需要知道何时需要检查规范,或者更好-何时重写代码以使其不依赖于序列点规则是否更简单的设计也可以。

于 2010-11-05T10:32:01.597 回答
0
int n,n_squared;
for(n=n_squared=0;n<100;n_squared+=n+ ++n)
 printf("%i squared might or might not be %i\n",n,n_squared);

......并不总是做你认为它会做的事情。这会使调试变得痛苦。原因是 ++n 检索、修改和存储 n 的值,这可能在检索 n 之前或之后。因此,n_squared 的值在第一次迭代后没有明确定义。序列点保证子表达式按顺序计算。

于 2010-12-28T22:06:41.300 回答