2016年阿里有一道关于c++的面试题(错题):
#include<iostream> void func(const int &v1,const int &v2) { std::cout<<v1<<” ”; std::cout<<v2 <<” ”; } int main(int argc ,char * argv[]) { int i=0; func(++i,i++); return 0; }
首先看到这个题的时候,对于很多人来说第一感觉就是茫然的感觉,答案全凭猜测,但其实搞清楚i++和i++的实质之后,你也就可以对这题发表看法了,确实存在着问题。
首先我们要弄懂++i和i++的区别,我们看下源码:
// ++i前缀形式: int& int::operator++() //这里返回一个引用形式,说明函数返回值可以作为一个左值使 用 { *this += 1; // 增加 return *this; // 返回值(数据和地址) }
很明显我们看到对于++i的操作,在源码中是先自增加,再返回这个变量的,当然包括了变量的值和对应的地址,也就是左值。
//后缀形式i++: const int int::operator++(int) //函数返回值是一个常量int型,与前缀形式的差别所在。 {//函数带参,说明有另外的空间开辟 int oldValue = *this; ++(*this); // 增加 return oldValue; // 返回被取回的值(只是数据) }
函数返回的是变量还未增加的值,注意这里返回的是一个新声明的变量,即只有数据值,但地址是不同的。也就是i++是一个右值
再看看func函数原型:
void func(const int &v1,const int &v2),形参是常量引用型,也就是说,传进去的是地址,并且是不可被修改的。这点很重要。
熟悉c++都知道:
1.c++不规定求职的顺序
2.同一函数调用的参数间没有序列点,不规定副作用(变量值变化)发生时机
3.ABI规定的入栈顺序,但并没规定谁先计算
所以,由于未定义行为,这道题其实是有问题的,核心问题就是求职顺序的不确定性。而不同的编译器可以有不同的编译顺序!
如果是右→左的顺序:
1. v2作为i的副本值的引用,v2=0;
2. i=i+1;//(i++)=1
3. i=i+1;//(++i)=2
4. V1为i的引用 //v1=i=2
而如果是左→右的顺序,不难知道v1=2;v2=1;