对于程序设计中经常用到的i++,++i语句,一般的教科书上都只用一段很简单的'先前++,后计算,再后++'来说明,但其实在程序运行中,并不是这样,关于i++与++i的执行效率问题,还有左值,右值的概念,本文将深度讨论i++与++i到底有什么区别.
先来看这样一道题目:(某企业面试题)
设有int a=1;那么下面几条语句哪些有错,哪些正确?
这道题目并不是考++a与a++的计算先后,而是考左值与右值的概念,那么我们用下面这个例子讲一下什么是左值,右值.还有i++与++i到底有什么区别.
我们有上面这样的C++代码,一个用了++a,另一个用到a++,然后使用g++ -S 将这个程序编译为汇编文件.得到下面的代码:
为了方便阅读,我把-4(%ebp)替换为a,-8(%ebp)替换为b.在汇编代码中,19~23行完成了C++代码的第6行,33~36行完成了C++代码的第8行,先不说别的,单从代码量上,我们就能得到第一个结论:++i比i++效率高.
然后再来看核心的a++与++a语句完成的位置,在19行,将a复制到eax中,然后使用leal给rax加1,放入到edx中.其实这块语句写成这样会比较好理解:
但是语句又增加一条.leal 1(%rax),%edx 完成的就是上面代码的2~3行,即把eax中的数据取出,加1后送到edx中.下面一条的movl %edx,a 就已经把a+1放入到a变量的内存地址中去了.即在第4行就完成了a++,之后使用的没有加1的a变量是保存在eax中的值.
再来看C++代码的第8行汇编结果,直接就对a变量加1,然后使用a变量计算,++a的完成位置就在33行.
由此我们可得出一个结论,++i与i++并不是教科书上写的那样,++i在计算之前加,i++在计算之后加,在表达式真正计算之前,就已经完成了++i和i++.他们的区别在于,使用i++时,程序会把没有做加法前的变量拷贝一份,在之后的表达式求值中使用这个副本来做计算,这就引出了'左值'和'右值'的概念.
用一句话来讲左值与右值,左值代表了一个变量,拥有自己内存地址的空间,而右值是一个常数,它并没有内存空间,我们只能给内存中写入数据,而不能给一个常量写入数据.
在++i中,由于直接操作对象就是i这个变量,所以说,++i返回的是一个左值.
而在i++中,在计算之前i已经加了1,我们操作的是它没有加1时的一个副本(或者说'快照'),它只是一个存在于寄存器中的一个临时常数,并没有自己的内存地址,因此不能对它赋值.所以说,i++返回一个右值.
所以,对于开始那个题目,显然C,D错误,因为我们不能给一个常量(右值)赋值.
最后的总结
1. ++i比i++执行效率高,如果只是想把变量加1的话,尽量使用++i.
2. ++i与i++都是在计算表达式之前就已经加1了的.
3. ++i返回左值,可写入,i++返回右值,不可写入.