C++ C++;-奇怪的是,输入被颠倒了

C++ C++;-奇怪的是,输入被颠倒了,c++,input,output,C++,Input,Output,我发现了一些奇怪的东西,我测试了以下代码: #include <iostream> using namespace std; int main() { int i=0, a[5]; cin>>a[i++]>>a[i++]>>a[i++]; for(int j=0; j<i; j++){ cout<<a[j]<<endl; } } 然后将输入反转如下: 3 2 1 我

我发现了一些奇怪的东西,我测试了以下代码:

#include <iostream>
using namespace std;

int main() {
    int i=0, a[5];
    cin>>a[i++]>>a[i++]>>a[i++];
    for(int j=0; j<i; j++){
        cout<<a[j]<<endl;
    }
}
然后将输入反转如下:

3
2
1
我认为它的输出应该与以下代码相同:

#include <iostream>
using namespace std;

int main() {
    int i=0, a[5];
    cin>>a[i++]; cin>>a[i++]; cin>>a[i++];
    for(int j=0; j<i; j++){
        cout<<a[j]<<endl;
    }
}
#包括
使用名称空间std;
int main(){
int i=0,a[5];
cin>>a[i++];cin>>a[i++];cin>>a[i++];

对于(int j=0;j这感觉像是未定义的行为和编译器依赖:

cin>>a[i++]>>a[i++]>>a[i++];

(大师们,如果我错了,请纠正我)

语句的行为
cin>>a[I++]>>a[I++]>>a[I++];
实际上没有定义。这是因为没有序列点

您不知道
i
何时递增,因此您的输出并不令人惊讶


在同一语句中修改和使用变量时,会导致未定义的行为,请参见。 在下面的语句中,我被修改了3次,并被访问了3次

cin>>a[i++]>>a[i++]>>a[i++];
我想下面的代码可以很好地工作

#include <iostream>
using namespace std;
int main() {
    int i=0, a[5];
    cin>>a[0]>>a[1]>>a[2];

    i=3;

    for(int j=0; j<i; j++){
    cout<<a[j]<<endl;
}
}
#包括
使用名称空间std;
int main(){
int i=0,a[5];
cin>>a[0]>>a[1]>>a[2];
i=3;
对于(int j=0;j
只是语法上的糖

cin.operator>>(a[i++]).operator>>(a[i++]).operator>>(a[i++]);
现在,对
operator>
的三个调用肯定是从左到右执行的,但是三个参数
a[i++]
可以按任何顺序计算。让我们调用参数
x
y
z

cin.operator>>(x).operator>>(y).operator>>(z);
您可能希望编译器将
x
y
z
替换为以下内容:

int& x = a[i];
i++;
int& y = a[i];
i++;
int& z = a[i];
i++;
但事实上,编译器给了你更多的自由。在你的例子中,它选择了:

int& z = a[i];
i++;
int& y = a[i];
i++;
int& x = a[i];
i++;
正如James Kanze指出的,它也可以选择:

int& x = a[i];
int& y = a[i];
int& z = a[i];
i++;
i++;
i++;

如前所述,代码具有未定义的行为,因为未指定函数参数的求值顺序,并且由于应用后增量运算符的副作用,因此未以确定性方式排序

我将如何解释结果

表情

cin>>a[i++]>>a[i++]>>a[i++];
如果我们使用函数表示法,则等价于下面的表达式

cin.operator >>( a[i++] ).operator >>( a[i++] ).operator >>( a[i++] );
函数参数的求值顺序没有指定。因此,一些编译器从右向左求值,而另一些编译器从左向右求值

很明显,编译器从右到左计算函数参数。第一个是最右边函数的参数

cin.operator >>( a[i++] ).operator >>( a[i++] ).operator >>( a[0] );
对参数求值后,编译器应用副作用。i等于1。然后编译器对第二个函数的参数求值,得到

cin.operator >>( a[i++] ).operator >>( a[1] ).operator >>( a[0] );
最后,在求值之后,第一个函数调用的参数将

cin.operator >>( a[2] ).operator >>( a[1] ).operator >>( a[0] );
变量i等于3

但是,正如我所说的,函数参数的求值顺序也没有指定,其他编译器可以将此表达式表示为

cin.operator >>( a[0] ).operator >>( a[1] ).operator >>( a[2] );

因此结果可能不同,程序的行为也未定义。

@juanchopanza是的,但在调用第一个
操作符>
时,
a[i++]
已经计算过的参数介于1和3之间。@FredOverflow这不会导致行为未指定吗?@juanchopanza参数的计算顺序确实未指定,但如果多个参数访问同一对象,并且其中至少有一个参数是写入的,则会出现未定义的行为(除非写访问和读访问之间有一个序列点)。@juanchopanza序列点(或C++11中的sequenced before概念)创建一个偏序。对
操作符>
的所有调用都是在提供其参数的
a[i++]
之后排序的,但是
a[i++]
在第一次调用之前。(对
操作符>>
的调用是有序的,因为存在数据依赖关系:第二次调用的第一个参数是第一次调用的返回值。)这是未定义的行为。我不确定在这里是否会使用表达式“编译器依赖”,因为这听起来更像是“实现定义的”(这意味着不同的实现可能会做不同的事情,但它们必须记录下来)。特别是,这里的结果可能会因优化标志的不同而有所不同,甚至会因周围语句中的代码而有所不同。实际上,考虑到我们有未定义的行为,编译器可以随意执行任何操作,包括生成崩溃的代码。(在某些情况下,G++会在识别未定义的行为时执行此操作。)更可能的是,编译器可以做一些与int&x=a[i]、&y=a[i]、&z=a[i];i+=3;
等效的事情。
cin.operator >>( a[2] ).operator >>( a[1] ).operator >>( a[0] );
cin.operator >>( a[0] ).operator >>( a[1] ).operator >>( a[2] );