99%都答不对的C++问题

下面问题的输出结果为?

	a = 0;
	cout << a++ << a++ << ++a << ++a << a++ << ++a << a++ << endl;

 
首先这个标题哗众取宠了,问题的实际意义也不大,只适合我这种渣渣分析学习。在学习类时,编写跟踪类的函数输出顺序的代码,发现一些奇怪的问题,然后抽象出来的问题。实际只是两方面的知识:cout的输出和前缀++和后缀++的重载。 

先看下面的问题,这是我另一个问题缩减版的一段代码:

int test(int j){
	cout << "joke " << j << endl;
	return j * j;
}
int main(){
	cout << "begin " << test(1) << test(2) << test(3) << " end\n";
	return 0;
}
cout的顺序我们可以从下面的汇编语言看出(VS10查看反汇编代码的方法是:调试中断的时候,点击“Debug” - “windows” - “Disassembly”或者快捷键Alt+8)


虽然还没学汇编,看的不太懂,我们也能发现cout的自右向左,先调用test(3)、再test(2)、再test(1),最终结果为:

刚看到结果,我还很疑惑,joke怎么能输出在begin的上面,不过现在略微能理解点了。

现在我们再想上边的

	a = 0;
	cout << a++ << a++ << ++a << ++a << a++ << ++a << a++ << endl;
同样,我们可以查看它的汇编代码,但是看不懂,怎么办。作为一个数学党,那我就总结归纳吧,多看几个从结果找规律呗。

	int a =0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0;
	cout << a++ << endl;
	cout << b++ << b++ << endl;
	cout << c++ << c++ << c++ << endl;
	cout << ++e << endl;
	cout << ++f << ++f << endl;
	cout << ++g << ++g << ++g << endl;
	a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0;
	cout << a++ << ++a << endl;
	cout << ++b << b++ << endl;
	cout << c++ << c++ << ++c << endl;
	cout << ++d << ++d << d++ << endl;
	cout << e++ << ++e << e++ << endl;
	cout << ++g << g++ << ++g << endl;
输出结果为:

我们发现,对于++x而言,输出的总是最大的值,即最终的值,而x++,输出的是当前的值(当然了,还是自右向左)。
为什么这样呢,学习类中重载前缀++和后缀++运算符的时候,我们知道前缀++的返回值是当前值的引用,后缀++的返回值是当前值的副本。
不妨放一段代码上来:

class Byte{
public:
	int b;
	const Byte& operator++(){    //前缀++
		b++;
		return *this;
	}
	const Byte operator++(int){  //后缀++
		Byte before(b);
		b++;
		return before;
	}
	Byte(int i = 0):b(i){}
};

我们看代码知道, 前缀++操作中:只需在++后,返回该对象的引用即可。在后缀++操作中,则需要在++之前创建当前对象的副本,在++之后返回新创建的副本对象(将返回值声明为const,防止编译器出现b++++这样的表达式)
我们注意到 前缀++返回的是对象的引用,后缀++返回的是对象递增之前的副本。
因此对于

	cout << c++ << c++ << c++ << endl;
输出结果就为210,而对于
	cout << ++g << ++g << ++g << endl;
输出结果就为333。
现在我们分析:

输出结果就为333。
现在我们分析:	cout << a++ << a++ << ++a << ++a << a++ << ++a << a++ << endl;
自右向左,第一个为0(因为递增前a=0,操作后a=1),第二个为*this(操作后a=2),第三个为2(因为递增前a=2,操作后a=3),第四个为*this,第五个为*this,第六个为5,第七个为6(操作后结果为7),因此*this的结果为7。按照这种分析,最终输出的结果应该为6577270,我们编译一下验证是否正确

完全正确,末了可以使用这个方法再测试一下(结果放在最后面)

	b = 0;
	cout << b++ << ++b << b++ << b++ << ++b << ++b << b++ << endl;

但是这些都是根据类中重载运算符进行分析,从汇编代码中应该会看的更清楚,可以对比a++和++e之间不同的汇编代码。(不会汇编,就先放在这里,万一哪个大神看到了可以指点渣渣)

	cout << a++ << endl;
013046E7  mov         eax,dword ptr [a]  
013046EA  mov         dword ptr [ebp-130h],eax  
013046F0  mov         ecx,dword ptr [a]  
013046F3  add         ecx,1  
013046F6  mov         dword ptr [a],ecx  
013046F9  mov         esi,esp  
013046FB  mov         edx,dword ptr [__imp_std::endl (130A330h)]  
01304701  push        edx  
01304702  mov         edi,esp  
01304704  mov         eax,dword ptr [ebp-130h]  
0130470A  push        eax  
0130470B  mov         ecx,dword ptr [__imp_std::cout (130A334h)]  
01304711  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (130A338h)]  
01304717  cmp         edi,esp  
01304719  call        @ILT+410(__RTC_CheckEsp) (130119Fh)  
0130471E  mov         ecx,eax  
01304720  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (130A320h)]  
01304726  cmp         esi,esp  
01304728  call        @ILT+410(__RTC_CheckEsp) (130119Fh)  
	cout << ++e << endl;
01304842  mov         eax,dword ptr [e]  
01304845  add         eax,1  
01304848  mov         dword ptr [e],eax  
0130484B  mov         esi,esp  
0130484D  mov         ecx,dword ptr [__imp_std::endl (130A330h)]  
01304853  push        ecx  
01304854  mov         edi,esp  
01304856  mov         edx,dword ptr [e]  
01304859  push        edx  
0130485A  mov         ecx,dword ptr [__imp_std::cout (130A334h)]  
01304860  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (130A338h)]  
01304866  cmp         edi,esp  
01304868  call        @ILT+410(__RTC_CheckEsp) (130119Fh)  
0130486D  mov         ecx,eax  
0130486F  call        dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (130A320h)]  
01304875  cmp         esi,esp  
01304877  call        @ILT+410(__RTC_CheckEsp) (130119Fh)  
类似的还有这篇文章 C++输出流cout的顺序问题,我们可以发现因为返回的副本,所以其实就是cout << i++<<i++<<i++<<i++;结果为4321。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值