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标准的解释更清晰,但两种语言都是正确的。
<<
)不是序列点。该行为已定义但未指定。表达式中评估“c”两次使用的相对顺序未指定。但是,如果将其转换为函数表示法,则如下所示:
cout.operator<<(c++).operator<<(c);
在评估函数参数和执行函数体之间存在序列点,并且函数体不是交错的,因此结果只是未指定的行为,而不是未定义的行为。
如果您没有重载运算符:
int c=0;
int a = c++ << c;
那么行为将是未定义的,因为在没有中间序列点的情况下修改和使用c
的值。
编辑:序列litb
提出的是错误的。标准规定(§1.9/17):“调用函数时(无论函数是否内联),在评估所有函数参数(如果有)之后,执行函数体中的任何表达式或语句之前都有一个序列点。”
这明确地写明了参数被评估,然后(紧接着)执行函数体。他建议的顺序,其中对一个函数的参数进行评估,然后对另一个函数的参数进行评估,然后执行两个函数体,似乎并不是预期的,但也不被禁止。然而,这并不会改变要求:“...在评估所有函数参数(如果有)之后,存在一个序列点...”
关于函数体执行的后续语言并不会在评估所有函数参数后删除对序列点的要求。所有其他评估,无论是函数体还是其他函数参数,都遵循该序列点。我可以像任何人一样苛刻和扭曲地误读明显意图(但未完全说明)-但我无法想象“在评估所有函数参数后存在一个序列点”如何被解读为意味着“在评估所有函数参数后不存在序列点。”
尼尔的观点当然是正确的:我上面使用的语法是针对成员函数的。对于非成员重载,语法更像是:
operator<<(operator<<(cout,c++), c);
这并不消除对序列点的要求。
就其未指定性而言,它真的很简单:在评估所有函数参数之后,存在一个序列点,因此一个函数调用的所有参数必须完全评估(包括所有副作用),然后可以评估另一个函数调用的参数(考虑来自其他函数调用的任何副作用)-- 但是没有要求哪个函数调用的参数必须先评估还是后评估,所以它可以是 c
,然后是 c++
,或者它可以是 c++
,然后是 c
-- 但必须是其中之一,不能是交错的。
c++
和c
是完全符合规范的。 - Johannes Schaub - litbop<<
有两个参数。按从右到左的顺序(作为一种选项),我首先评估c
,然后继续评估op<<(cout, c++)
。好的,当我转移到op<<(cout, c++)
的参数时,我还没有到达任何一个函数序列点。从右到左,我评估c++
,但我仍然没有到达函数调用隐含的任何序列点。这肯定是未定义行为吧? - CB Baileyf(g(a,b),c)
中,标准规定必须在计算 g
之前计算 a
和 b
,并且必须在计算 f
之前计算 g
和 c
。但这只是部分顺序。计算顺序可以是:c
、b
、a
、g
、f
,或者 b
、c
、a
、g
、f
,或者 b
、a
、g
,如此类推。现在,如果您将 f == g == operator<<
,a == cout
,b == c++
,c == c
(最后一个很容易!),那么就有不同的组合方式允许在执行 c
之前或之后执行 c++
。 - David Rodríguez - dribeas它未定义的原因是编译器可以按任意顺序计算函数参数。考虑一下如果你正在调用一个函数(因为你正在这样做,但在函数语法中更容易设想):
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。