我认为它应该是 01 但有人说它“未定义”,有什么理由吗?
c++
既是增量也是赋值。何时发生赋值(在该行上的其他代码之前或之后)由编译器自行决定。它可以在之后cout <<
或之前发生。
这可以在 C99 标准 http://www.open-std.org/JTC1/SC22/wg14/www/docs/n1124.pdf中找到
您可以在 pdf 的第 28 页或第 5.1.2.3 节中找到它
p 的实际增量可以发生在前一个序列点和下一个序列点之间的任何时间
由于有人要求 C++ 标准(因为这是一个 C++ 问题),它可以在第 1.9.15 页第 10 页(或 pdf 格式的第 24 页)中找到
单个运算符的操作数和单个表达式的子表达式的计算是无序的
它还包括以下代码块:
i = v[i++]; // the behavior is undefined
i = 7, i++, i++; // i becomes 9
i = i++ + 1; // the behavior is undefined
我觉得C99标准的解释更清楚,但在两种语言中都是如此。
5 回答
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 the cout <<
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
the actual increment of p can occur at any time between the previous sequence point and the next sequence point
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)
evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced
It also includes the following code block:
i = v[i++]; // the behavior is undefined
i = 7, i++, i++; // i becomes 9
i = i++ + 1; // the behavior is undefined
I feel that the C99 standard's explanation is clearer, but it is true in both languages.
如果您修改一个值然后在没有中间序列点的情况下读取它(或尝试再次修改它),这是未定义的行为。C++ 中序列点的概念有点技术性(您可以在这里阅读一些内容),但最重要的是流插入 ( <<
)不是序列点。
这是未定义行为的原因是,在没有序列点的情况下,允许编译器以它认为合适的任何方式重新排序操作。也就是说,允许检索 的值c
(并在第二次插入时保留它),然后执行后续c++
操作以获取第一次插入的值。因此,您无法确定增量c
是在确定第二次插入的值之前还是之后发生。
行为已定义但未指定。未指定评估表达式中“c”的两种用法的相对顺序。但是,如果将其转换为函数符号,则如下所示:
cout.operator<<(c++).operator<<(c);
在评估函数的参数和执行函数体之间存在一个序列点,并且函数体没有交错,因此结果只是未指定的,而不是未定义的行为。
如果您没有重载运算符:
int c=0;
int a = c++ << c;
那么行为将是未定义的,因为修改和使用 的值都c
没有中间的序列点。
编辑:提出的顺序litb
是完全错误的。该标准规定(第 1.9/17 节):“调用函数时(无论该函数是否是内联函数),在所有函数参数(如果有)的评估之后都有一个序列点,该序列点发生在执行任何表达式或函数体中的语句。”
这清楚地写了一个想法,即评估参数,然后(立即)执行函数的主体。他建议的顺序,其中一个函数的参数被评估,然后是另一个函数的参数,然后两个函数体的执行似乎不是有意的,但也没有被禁止。然而,这并没有改变 - 要求仍然是:“......在评估所有函数参数(如果有的话)之后有一个序列点......”
在评估所有函数参数之后,关于执行主体的后续语言不会删除对序列点的要求。所有其他评估,无论是函数体还是其他函数参数,都遵循该序列点。我可能像任何人一样迂腐和反常地误读了明确的意图(但没有完全说明)——但我无法想象“在评估所有函数参数之后有一个序列点”如何被解读为意义“在评估所有函数参数之后没有序列点。”
Neil 的观点当然是正确的:我上面使用的语法是针对成员函数的。对于非成员重载,语法更像:
operator<<(operator<<(cout,c++), c);
不过,这也不会消除对序列点的要求。
至于未指定:实际上非常简单:在评估所有函数参数之后有一个序列点,因此必须完全评估一个函数调用的所有参数(包括所有副作用),然后另一个函数调用的参数可以是评估(考虑到其他的任何副作用)——但没有要求必须首先或第二次评估哪个函数调用的参数,所以它可以是c
, then c++
,或者它可能是c++
, then c
——但它必须是一个或另一个,而不是交错。
它未定义的原因是编译器可以自由地以任何顺序计算函数参数。考虑你是否在哪里调用函数(因为你是,但在函数语法中更容易想象):
cout.output(c++).output(c);
编译器可能会以相反的顺序、前向或其他方式命中参数。它可以在计算第二个输出的参数之前调用第一个输出,或者它可以同时执行两个然后调用。
如我所见, 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
#include <stdio.h>
void f(int a, int b) {
printf("%d %d\n",a,b);
}
int main(int argc, char **argv) {
int c = 0;
f(c++,c++);
return 0;
}
在 C++ 中
#include <iostream>
int main(int argc, char **argv) {
int c = 0;
std::cout << c++ << " " << c++ << std::endl;
return 0;
}
很有趣,因为 gcc 和 g++ 编译结果为 1 0,而 clang 编译结果为 0 1