int c=0的结果; cout<
我认为它应该是 01 但有人说它“未定义”,有什么原因吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web
技术交流群。
我认为它应该是 01 但有人说它“未定义”,有什么原因吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
c++
既是增量又是赋值。何时发生赋值(在该行的其他代码之前或之后)由编译器自行决定。它可以发生在cout <<
之后或之前。这个可以在C99标准中找到
http://www.open-std.org/JTC1 /SC22/wg14/www/docs/n1124.pdf
您可以在 pdf 的第 28 页或第 5.1.2.3 节中找到它
由于有人询问 C++ 标准(因为这是一个 C++ 问题),因此可以在第 1.9.15 节第 10 页(或第 24 页)中找到它pdf 格式)
它还包括以下代码块:
我觉得 C99 标准的解释更清晰,但在两种语言中都是如此。
c++
is both an increment and an assignment. When the assignment occurs (before or after other code on that line) is left up to the discretion of the compiler. It can occur after thecout <<
or before.This can be found in the C99 standard
http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf
You can find it on page 28 in the pdf or section 5.1.2.3
Since someone asked for the C++ standard (as this is a C++ question) it can be found in section 1.9.15 page 10 (or 24 in pdf format)
It also includes the following code block:
I feel that the C99 standard's explanation is clearer, but it is true in both languages.
如果您修改一个值,然后在没有中间序列点的情况下读取它(或尝试再次修改它),则这是未定义的行为。 C++ 中序列点的概念有点技术性(您可以在此处阅读一些相关内容),但底线是流插入 (
<<
) 不是序列点。这是未定义行为的原因是,在没有序列点的情况下,编译器可以以任何它认为合适的方式重新排序操作。也就是说,允许检索
c
的值(并在第二次插入时保留它),然后后执行c++
以获取第一次插入的值。因此,您无法确定增量是在第二次插入的c
值确定之前还是之后发生。It is undefined behavior if you modify a value and then read it (or try to modify it again) without an intervening sequence point. The concept of a sequence point in C++ is a bit technical (you can read a little about it here), but the bottom line is that stream insertion (
<<
) is not a sequence point.The reason why this is undefined behavior is because, in the absence of a sequence point, the compiler is allowed to re-order operations in any way it sees fit. That is, it is permitted to retrieve the value of
c
(and hold onto it for the second insertion) and then afterwords executec++
to get the value for the first insertion. So you can't be sure whether the increment will occur before or after the value ofc
for the second insertion is determined.它未定义的原因是编译器可以自由地以任何顺序计算函数参数。考虑一下您是否在哪里调用函数(因为确实如此,但是当它采用函数语法时更容易想象):
编译器可能会以相反的顺序、正向的顺序或其他方式命中参数。它可以在计算第二个输出的参数之前调用第一个输出,或者它可以同时执行这两个操作然后调用。
The reason it is undefined is that the compiler is free to calculate function parameters in any order. Consider if you where calling a function (because you are, but it's easier to envision when it's in function syntax):
The compiler may hit the parameters in reverse order, forward order, or whatever. It may call the first output before calculating the parameter to the second output or it may do both and then call.
行为已定义但未指定。未指定计算表达式中两次使用“c”的相对顺序。但是,如果将其转换为函数表示法,它看起来像这样:
在计算函数参数和执行函数体之间存在一个序列点,并且函数体不会交错,因此结果只是未指定的,不是未定义的行为。
如果您没有重载运算符:
那么行为将是未定义的,因为修改和使用
c
的值都没有插入序列点。编辑:
litb
带来的序列完全是错误的。该标准规定(§1.9/17):“当调用函数时(无论函数是否内联),在所有函数参数(如果有)的求值之后有一个序列点,该序列点发生在任何表达式或执行之前函数体中的语句。”这清楚地写出了这样的想法:对参数进行求值,然后(紧接着)执行函数体。他建议的顺序是,先评估一个函数的参数,然后评估另一个函数的参数,然后执行两个函数体,这似乎不是有意的,但也没有被禁止。然而,这并没有改变什么——要求仍然是:“...在计算所有函数参数(如果有)之后有一个序列点...”
关于主体执行的后续语言不会删除评估所有函数参数后需要序列点。所有其他计算,无论是函数体还是其他函数参数,都遵循该序列点。我可能会像任何人一样迂腐和反常,误读了明确的意图(但没有完全说明)——但我无法想象“在评估所有函数之后有一个序列点”参数”可以理解为“在计算所有函数参数之后不存在序列点”。
尼尔的观点当然是正确的:我上面使用的语法适用于成员函数。对于非成员重载,语法将更像:
但这也不会消除对序列点的要求。
就其未指定而言:实际上非常简单:在评估所有函数参数之后有一个序列点,因此必须完全评估一个函数调用的所有参数(包括所有副作用),然后可以对另一个函数调用的参数进行评估已评估(考虑到其他函数的任何副作用)——但是没有要求必须首先或第二评估哪个函数调用的参数,因此可以是
c
,然后是c++,或者可以是
c++
,然后是c
——但必须是其中之一,而不是交错。The behavior is defined but unspecified. The relative order of evaluating the two uses of 'c' in the expression isn't specified. However, if you convert it to functional notation, it looks like this:
There's a sequence point between evaluating the arguments to a function, and executing the body of the function, and function bodies aren't interleaved, so the result is only unspecified, not undefined behavior.
If you didn't have an overloaded operator:
Then the behavior would be undefined, because both modifying and using the value of
c
without an intervening sequence point.Edit: The sequence
litb
brings up is simply wrong. The standard specifies (§1.9/17): "When calling a function (whether or not the function is inline), there is a sequence point after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body."This clearly written with the idea that arguments are evaluated, then (immediately afterward) the body of the function is executed. The sequence he suggests, in which arguments to one function are evaluated, then arguments to another, then execution of both function bodies doesn't seem to have been intended, but also isn't prohibited. That, however, changes nothing -- the requirement is still that: "...there is a sequence point after the evaluation of all function arguments (if any)..."
The subsequent language about execution of the body does NOT remove the requirement for a sequence point after evaluating all function arguments. All other evaluation, whether of the function body or other function arguments follows that sequence point. I can be as pedantic and perverse as anybody about mis-reading what's clearly intended (but not quite stated) -- but I can't imagine how "there is a sequence point after the evaluation of all function arguments" can be read as meaning "there is NOT a sequence point after the evaluation of all function arguments."
Neil's point is, of course, correct: the syntax I've used above is for member functions. For a non-member overload, the syntax would be more like:
This doesn't remove the requirement for sequence points either though.
As far as it being unspecified: it's pretty simple really: there's a sequence point after evaluating all function arguments, so all the arguments for one function call must be fully evaluated (including all side effects), then arguments for the other function call can be evaluated (taking into account any side effects from the other) -- BUT there's no requirement about WHICH function call's arguments must be evaluated first or second, so it could be
c
, thenc++
, or it could bec++
, thenc
-- but it has to be one or the other, not an interleaving.据我看来,
f(c++);
相当于:
f(c); c+=1;
和
f(c++,c++);
相当于:
f(c,c); c+=1; c+=1;
但情况可能是这样的
f(c++,c++);
变成
f(c,c+1); c+= 2;
使用 gcc 和 clang 进行的实验
首先在 C和 C++ 中
很有趣,因为 gcc 和 g++ 编译结果为
1 0
而 clang 编译结果为
0 1
As I see it,
f(c++);
is equivalent to:
f(c); c += 1;
And
f(c++,c++);
is equivalent to:
f(c,c); c += 1; c += 1;
But it may be the case that
f(c++,c++);
becomes
f(c,c+1); c+= 2;
An experiment with gcc and clang, first in C
and in C++
Is interesting, as gcc and g++ compiled results in
1 0
whereas clang compiled results in
0 1