最近,我看到很多关于输出的问题,一些疯狂但语法上允许的代码语句,如i = ++i + 1
和i=(i,i++,i)+1;
。
实际上坦率地说,几乎没有人在实际编程中写任何这样的代码。坦率地说,我从未在我的专业经验中遇到任何这样的代码。所以我通常最终会在SO上跳过这些问题。但是最近这种问题的绝对数量让我想到如果我错过了一些重要的理论,就是跳过这样的问题。我认为这样的Q围绕着Sequence points
。我几乎不知道序列点是坦率的,我只是想知道如果不知道它在某种程度上是一个障碍。那么有人可以解释Sequence points
的理论/概念,或者如果可能的话,指向解释这个概念的资源。此外,是否值得花时间了解这个概念/理论?
答案 0 :(得分:5)
我能想到的最简单的答案是:
C ++是根据抽象机器定义的。在抽象机器上执行的程序的输出仅根据执行“副作用”的顺序来定义。副作用定义为对IO库函数的调用,以及对标记为volatile的变量的更改。
允许C ++编译器在内部做任何他们想做的事情来优化代码,但它们不能改变写入volatile变量和io调用的顺序。
序列点定义c / c ++程序的心跳 - 在序列点“完成”之前的副作用,并且序列点之后的副作用尚未发生。但是,副作用(或者可以间接影响副作用的代码(在序列点内)可以重新排序。
这就是为什么理解它们很重要。如果没有这种理解,你对c ++程序的基本理解(以及如何通过激进的编译器进行优化)是有缺陷的。
答案 1 :(得分:2)
请参阅http://en.wikipedia.org/wiki/Sequence_point。
这是一个非常简单的概念,所以你不需要投入太多时间:)
答案 2 :(得分:2)
序列点的确切技术细节可能会变得毛茸茸,是的。但遵循这些指南几乎解决了所有实际问题:
此处“修改”包括=
,+=
等左侧值的分配操作,以及++x
,x++
,{{1 }和--x
语法。 (这通常是这些增量/减量表达式,其中有些人试图变得聪明并最终陷入麻烦。)
幸运的是,大多数“预期”地方都有序列点:
x--
和&&
运营商。||
。?
逗号运算符。 (最常见于条件,例如,
。)分隔函数参数的逗号是不逗号运算符,并且不是序列点。重载for (a=0, b=0; a<m && b<n; ++a, ++b)
,operator&&
和operator||
不会导致序列点。这一事实可能带来的惊喜是通常不鼓励过载的原因之一。
答案 3 :(得分:1)
值得知道的是,序列点存在是因为如果您不了解它们,您可以轻松编写似乎在测试中运行良好但实际上未定义的代码,并且当您在另一台计算机上运行或使用不同的编译时可能会失败选项。特别是如果您编写例如x++
作为包含x
的较大表达式的一部分,则很容易遇到问题。
我认为没有必要完全学习所有规则 - 但是您需要知道何时需要检查规范,或者更好 - 何时重写代码以使其不依赖关于序列点规则,如果更简单的设计也可以。
答案 4 :(得分: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的值。序列点保证按顺序评估子表达式。