最近,我看到很多关于一些疯狂但语法上允许的代码语句的输出的问题,例如 i = ++i + 1
和 i=(i,i++,i) +1;
。
坦率地说,现实中几乎没有人在实际编程中编写任何此类代码。坦白说,在我的专业经验中,我从未遇到过此类代码。所以我通常最终会跳过这样的问题。但最近被问到的此类问题的数量之多让我思考我是否因跳过此类问题而错过了一些重要的理论。我推测此类 Q 围绕序列点
。坦率地说,我对序列点几乎一无所知,我只是想知道不了解它是否会在某种程度上成为一种障碍。那么有人可以解释一下序列点
的理论/概念吗?或者如果可能的话请指出解释该概念的资源。另外,是否值得投入时间来了解这个概念/理论?
Lately, I have seen a lot of questions being asked about output for some crazy yet syntactically allowed code statements like like i = ++i + 1
and i=(i,i++,i)+1;
.
Frankly realistically speaking hardly anyone writes any such code in actual programing.To be frank I have never encountered any such code in my professional experience. So I usually end up skipping such questions here on SO. But lately the sheer volume of such Q's being asked makes me think if I am missing out some important theory by skipping such Q's. I gather that the such Q's revolve around Sequence points
. I hardly know anything about sequence points to be frank and I am just wondering if not knowing about it is a handicap in some way. So can someone please explain the theory /concept of Sequence points
, or If possible point to a resource which explains about the concept. Also, is it worth to invest time in knowing about this concept/theory?
发布评论
评论(5)
是的,序列点的确切技术细节可能会变得很复杂。但遵循这些准则几乎可以解决所有实际问题:
这里的“修改”包括
=
、+=
等中左侧值的赋值操作,也包括++x
,x++
、--x
和x--
语法。 (通常是这些递增/递减表达式,有些人试图变得聪明,但最终陷入了麻烦。)幸运的是,在大多数“预期”位置都有序列点:
&&
和||
运算符处。?
处。,
逗号运算符处。 (最常见于 for 条件,例如for (a=0, b=0; a。)分隔函数参数的不是逗号运算符,也不是序列点。
重载的
operator&&
、operator||
和operator,
不会导致序列点。这一事实带来的潜在惊喜是通常不鼓励超载的原因之一。The exact technical details of sequence points can get hairy, yes. But following these guideline solves almost all the practical issues:
Here "modification" includes assignment operations on the left-hand value in
=
,+=
, etc., and also the++x
,x++
,--x
, andx--
syntaxes. (It's usually these increment/decrement expressions where some people try to be clever and end up getting into trouble.)Luckily, there are sequence points in most of the "expected" places:
&&
and||
operators.?
in a ternary expression.,
comma operator. (Most commonly seen in for conditions, e.g.for (a=0, b=0; a<m && b<n; ++a, ++b)
.) A comma which separates function arguments is not the comma operator and is not a sequence point.Overloaded
operator&&
,operator||
, andoperator,
do not cause sequence points. Potential surprises from that fact is one reason overloading them is usually discouraged.值得了解序列点的存在,因为如果您不了解它们,您可以轻松编写在测试中似乎运行良好但实际上未定义的代码,并且当您在另一台计算机上或使用不同的编译选项运行它时可能会失败。特别是,如果您将
x++
编写为还包含x
的较大表达式的一部分,则很容易遇到问题。我认为没有必要完全学习所有规则 - 但您需要知道何时需要检查规范,或者更好 - 何时重写代码以使其不依赖于序列点规则是否更简单的设计也可以工作。
It is worth knowing that sequence points exist because if you don't know about them you can easily write code which seems to run fine in testing but actually is undefined and might fail when you run it on another computer or with different compile options. In particular if you write for example
x++
as part of a larger expression that also includesx
you can easily run into problems.I don't think it is necessary to learn all the rules fully - but you need to know when you need to check the specification, or perhaps better - when to rewrite your code to make it so that you aren't relying on sequence points rules if a simpler design would work too.
...并不总是做你认为会做的事。这会让调试变得很痛苦。
原因是 ++n 检索、修改和存储 n 的值,该值可能是在检索 n 之前或之后。因此,n_squared 的值在第一次迭代后没有明确定义。序列点保证子表达式按顺序求值。
... doesn't always do what you think it will do. This can make debugging painful.
The reason is the ++n retrieves, modifies, and stores the value of n, which could be before or after n is retrieved. Therefore, the value of n_squared isn't clearly defined after the first iteration. Sequence points guarantee that the subexpressions are evaluated in order.
我能想到的最简单的答案是:
C++ 是根据抽象机定义的。在抽象机上执行的程序的输出仅根据“副作用”执行的顺序来定义。副作用被定义为对 IO 库函数的调用,以及对标记为 volatile 的变量的更改。
C++ 编译器可以在内部做任何他们想做的事情来优化代码,但他们不能改变 volatile 变量和 io 调用的写入顺序。
序列点定义了c/c++程序的心跳——序列点之前的副作用是“完成”的,而序列点之后的副作用尚未发生。但是,副作用(或者,可以间接影响副作用的代码(在序列点内)可以重新排序。
这就是为什么理解它们很重要。如果没有这种理解,您对什么的基本理解C++ 程序(以及如何通过激进的编译器对其进行优化)是有缺陷的。
The simplest answer I can think of is:
C++ is defined in terms of an abstract machine. The output of a program executed on the abstract machine is defined ONLY in terms of the order that "side effects" are performed. And Side effects are defined as calls into IO library functions, and changes to variables marked volatile.
C++ compilers are allowed to do whatever they want internally to optimize code, but they cannot change the order of writes to volatile variables, and io calls.
Sequence points define the c/c++ program's heartbeat - side effects before the sequence point are "complete" and side effects after the sequence point have not yet taken place. But, side effects (or, code that can effect a side effect indirectly( within a sequence point can be re-ordered.
Which is why understanding them is important. Without that understanding, your fundamental understanding of what a c++ program is (And how it might be optimized by an agressive compiler) is flawed.
请参阅http://en.wikipedia.org/wiki/Sequence_point。
这是一个非常简单的概念,因此您不需要投入太多时间:)
See http://en.wikipedia.org/wiki/Sequence_point.
It's a quite simple concept, so you don't need to invest much time :)