序列点和偏序
几天前,这里讨论了表达式是否
i = ++i + 1
调用 UB (未定义的行为)与否。
最后得出的结论是,当“i”的值在两个序列点之间多次更改时,它会调用 UB。
我在同一线程中参与了与 Johannes Schaub 的讨论。据他说
i=(i,i++,i)+1 ------ (1) /* 也调用 UB */
我说 (1) 不会调用 UB,因为前面的子表达式的副作用已被逗号清除i 和 i++ 之间以及 i++ 和 i 之间使用运算符 ','。
然后他给出了如下解释:
“是的,i++ 完成之前的所有副作用之后的序列点,但是没有什么可以阻止赋值副作用与 i++ 的副作用重叠。根本问题是赋值的副作用是没有指定发生在赋值的两个操作数的求值之后或之前,因此序列点不能做任何关于保护这一点的事情:序列点导致偏序:仅仅因为 i++ 之后和之前都有一个序列点,则不会意味着所有副作用都根据 i 排序。
此外,请注意,仅仅一个序列点没有任何意义:求值的顺序并不由代码的形式决定。它是由语义规则决定的。在这种情况下,没有语义规则说明在评估其操作数或这些操作数的子表达式时何时发生赋值副作用”。
用“粗体”写的声明让我很困惑。据我所知:
“在执行序列中称为序列点的某些指定点,先前评估的所有副作用都应完成,并且后续评估的副作用不会发生。”
因为,逗号运算符还指定执行顺序,所以当我们到达最后一个 i 时,i++ 的副作用已被取消。如果未指定求值顺序,He(Johannes) 是正确的(但在逗号运算符的情况下,这是很好的)
所以我只想知道(1)是否调用UB?。有人可以给出另一个有效的解释吗?
谢谢!
A few days back there was a discussion here about whether the expression
i = ++i + 1
invokes UB
(Undefined Behavior) or not.
Finally the conclusion was made that it invokes UB as the value of 'i' is changing more than once between two sequence points.
I was involved in a discussion with Johannes Schaub in that same thread. According to him
i=(i,i++,i)+1 ------ (1) /* invokes UB as well */
I said (1) does not invoke UB because the side effects of the previous subexpressions are cleared by the comma operator ',' between i and i++ and between i++ and i.
Then he gave the following explanation:
"Yes the sequence point after i++ completes all side effects before it, but there is nothing that stops the assignment side effect overlapping with the side effect of i++.The underlying problem is that the side effect of an assignment is not specified to happen after or before the evaluation of both operands of the assignment, and so sequence points cannot do anything with regard to protecting this: Sequence points induce a partial order: Just because there is a sequence point after and before i++ doesn't mean all side effects are sequenced with regard to i.
Also, notice that merely a sequence point means nothing: The order of evaluations isn't dictated by the form of code. It's dictated by semantic rules. In this case, there is no semantic rule saying when the assignment side effect happens with regard to evaluating both of its operands or subexpressions of those operands".
The statement written in "bold" confused me. As far as I know:
"At certain specified points in the execution sequence called sequence points,all side effects of previous evaluations shall be complete and no side effects of subsequent evaluations shall have taken place."
Since,comma operators also specify execution order the side effect of i++ have been cancelled when we reach the last i.He(Johannes) would have been right had the order of evaluation been not specified(but in case of comma operator it is well specified).
So I just want to know whether (1) invokes UB or not?. Can someone give another valid explanation?
Thanks!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
C 标准对赋值运算符有这样的规定(C90 6.3.16 或 C99 6.5.16 赋值运算符):
在我看来,在语句中:
赋值运算符“前一个”的序列点将是第二个逗号运算符,“下一个”序列点将是表达式的末尾。所以我想说该表达式不会调用未定义的行为。
但是,此表达式:
将具有未定义的行为,因为赋值运算符的 2 个操作数的求值顺序未定义,在这种情况下,问题不是发生赋值运算符的副作用,而是您不这样做知道左句柄操作数中使用的 i 值是在右手边之前还是之后计算。这种求值顺序问题在第一个示例中不会出现,因为在该表达式中,
i
的值实际上并未在左侧使用 - 赋值运算符感兴趣的是i
的“左值”。但我也认为所有这些都足够粗略(而且我对所涉及的细微差别的理解也足够粗略),如果有人能够以其他方式说服我(无论是哪一方面),我都不会感到惊讶。
The C standard says this about assignment operators (C90 6.3.16 or C99 6.5.16 Assignment operators):
It seems to me that in the statement:
the sequence point 'previous' to the assignment operator would be the second comma operator and the 'next' sequence point would be the end of the expression. So I'd say that the expression doesn't invoke undefined behavior.
However, this expression:
would have undefined behavior because the order of evaluation of the 2 operands of the assignment operator is undefined, and in this case instead of the problem being when the assignment operator's side effect takes place, the problem is you don't know whether the value of i used in the left handle operand will be evaluated before or after the right hand side. This order of evaluation problem doesn't occur in the first example because in that expression the value of
i
isn't actually used in the left-hand side - all that the assignment operator is interested in is the "lvalue-ness" ofi
.But I also think that all this is sketchy enough (and my understanding of the nuances involved are sketchy enough) that I wouldn't be surprised if someone can convince me otherwise (on either count).
i=(i,i++,i)+1 ------ (1) /* 也调用 UB */
它不会调用未定义的行为。
i++
的副作用将在评估下一个序列点(由其后面的逗号表示)之前以及赋值之前发生。不过,语言数独不错。 :-)
编辑:此处有更详细的解释。
i=(i,i++,i)+1 ------ (1) /* invokes UB as well */
It does not invoke undefined behaviour. The side effect of
i++
will take place before the evaluation of the next sequence point, which is denoted by the comma following it, and also before the assignment.Nice language sudoku, though. :-)
edit: There's a more elaborate explanation here.
我相信下面的表达式肯定有未定义的行为。
原因是逗号运算符指定了括号中的子表达式之间的序列点,但没有指定该序列中
+
左侧操作数的求值发生的位置。一种可能性是在i++
周围的序列点之间,这违反了 5/4,因为i
被写入两个序列点之间,但也在相同的序列点之间被读取两次,并且不仅要确定要存储的值,还要确定+
运算符的第一个操作数的值。这也有未定义的行为。
现在,我对这个说法不太确定。
尽管适用相同的原则,
i
必须被“评估”为可修改的左值,并且可以随时这样做,但我不相信它的值永远是阅读作为其中的一部分。 (或者表达式是否违反了另一个限制而导致 UB?)子表达式
(i, i++, i)
作为确定要存储的值的一部分而发生,并且该子表达式包含将值存储到i
后的序列点。我看不出有任何方法可以在确定要存储的值之前完成 i++ 的副作用,因此可以最早可能发生赋值副作用。在此序列点之后,
i
的值最多读取一次,并且仅用于确定将存储回i
的值,因此最后一部分没问题。I believe that the following expression definitely has undefined behaviour.
The reason is that the comma operator specifies sequence points between the subexpressions in parentheses but does not specify where in that sequence the evaluation of the left hand operand of
+
occurs. One possibility is between the sequence points surroundingi++
and this violates the 5/4 asi
is written to between two sequence points but is also read twice between the same sequence points and not just to determine the value to be stored but also to determine the value of the first operand to the+
operator.This also has undefined behaviour.
Now, I am not so sure about this statement.
Although the same principals apply,
i
must be "evaluated" as a modifiable lvalue and can be done so at any time, but I'm not convinced that its value is ever read as part of this. (Or is there another restriction that the expression violates to cause UB?)The sub-expression
(i, i++, i)
happens as part of determining the value to be stored and that sub-expression contains a sequence point after the storage of a value toi
. I don't see any way that this wouldn't require the side effect ofi++
to be complete before the determination of the value to be stored and hence the earliest possible point that the assignment side effect could occur.After this sequnce point
i
's value is read at most once and only to determine the value that will be stored back toi
, so this last part is fine.