i++和++i的那些陷阱坑
来源:互联网 发布:淘宝分销订单怎么处理 编辑:程序博客网 时间:2024/06/07 01:52
一般意义上的理解,++i是先定义一个i的副本,将i执行+1,最后返回那个副本; i++是将i执行+1,然后返回i的引用。
前缀版本++i和后缀版本i++,他们对操作数的影响是一样的,但是影响的时间不同。这就像对于钱包来说,清理草坪之前付钱和清理草坪之后付钱的最终结果是一样的,但支付钱的时间不同。(《C++Primer Plus》5.1)
接下来用两个代码例子说明一下:
例1:
#include <stdio.h> int main() { int i=0; printf("%d,%d,%d,%d\n",i++,--i,++i,i--); return 0; }
按照最初的理解,前置操作是将i放在寄存器中,然后进行操作,最后通过寄存器返回;后置是将值放在临时量中,输出是直接从临时量中取值。函数运行需要从右往左向栈中压入参数,所以猜想结果是0,-1,-1,-1。
但是编译器输出是-1,0,0,0。
先不看结果如何,应该想一想这个操作执行的过程。
接下来,从反汇编代码看一看
int i=0; 0120437E mov dword ptr [i],0 //把0赋值给i printf("%d,%d,%d,%d\n",i++,--i,++i,i--); //参数从右向左压栈01204385 mov eax,dword ptr [i] //把i的值放在寄存器eax01204388 mov dword ptr [ebp-0D0h],eax //把寄存器中的值放在ebp-0D0h位置处的临时内存区域(临时量) 0120438E mov ecx,dword ptr [i] //把i的值放在ecx中 01204391 sub ecx,1 //寄存器ecx中的值-101204394 mov dword ptr [i],ecx //ecx中取出i的值 01204397 mov edx,dword ptr [i] //把i的值放在寄存器edx中0120439A add edx,1 //寄存器edx中的值+10120439D mov dword ptr [i],edx //将edx中的值放在i的存储区域中,更新++i 012043A0 mov eax,dword ptr [i] //把i的值放进寄存器eax012043A3 sub eax,1 //对eax中的值-1012043A6 mov dword ptr [i],eax //将eax中的值放在i的存储区域中,更新i--012043A9 mov ecx,dword ptr [i] //把i的值放在寄存器ecx中 printf("%d,%d,%d,%d\n",i++,--i,++i,i--); 012043AC mov dword ptr [ebp-0D4h],ecx //把寄存器中的值放在ebp-0D0h位置处的临时内存区域(临时量)012043B2 mov edx,dword ptr [i] //把i的值放在edx中012043B5 add edx,1 //对edx中的值+1012043B8 mov dword ptr [i],edx //将edx中值放在i的存储区域中,更新i++012043BB mov esi,esp 012043BD mov eax,dword ptr [ebp-0D0h] //参数的值确定好了之后,开始进行压栈操作。 将临时区域中的值放在eax中012043C3 push eax //将 i-- 的结果压栈。012043C4 mov ecx,dword ptr [i] //到i的区域取值;012043C7 push ecx //++i012043C8 mov edx,dword ptr [i] //前置操作是到自己的存储区域取值012043CB push edx //--i012043CC mov eax,dword ptr [ebp-0D4h] 012043D2 push eax //i++ 取出临时区域的值012043D3 push 120CC6Ch 012043D8 call dword ptr ds:[12103B8h] 012043DE add esp,14h 012043E1 cmp esi,esp 012043E3 call __RTC_CheckEsp (012012D0h)
看完汇编代码过程,应该明白它的结果了。
将后置++/–的值放在了一个临时区域内(而不是用寄存器存),然后再对变量的存储区域进行+1/-1。 而前置++/–的值直接经过计算将结果存入到变量的存储区域内。
例2:简单实现了一个Int类
在C++中,我们对自定义的类实现重载操作符,需要满足内置类型一样的操作,例如前置操作和后置操作。
class Int{public: Int(int val) { _val = val; } Int& operator++()//前置++ { ++_val; return *this; } const Int operator++(int)//后置++ { Int tmp = *this; ++(*this);//利用前置++ return tmp; }private: int _val;};
在实现前置++时,直接进行++,在自己的存储区域取值,返回*this;
后置++因为是先赋值在++,所以要返回自增前的对象,先拷贝一份然后自增,返回拷贝的对象。
看到这里,不由地想到几个问题或者是疑问。
1.后置++重载为什么要加一个参数int ??
构成重载的条件之一是:在同一作用域中,函数名相同,参数列表不同。 这里前置++和后置++的重载实现,因为在相同作用域下,函数名相同,多加一个参数int是为了重载实现,绕过语法限制。
2.前置++为什么返回类型是引用?后置++为什么返回const对象??
返回引用是:(1)去除临时量;(2)可能会赋值
后置++如果不是const,当编译器遇到i(++)++时也会让它通过运行,理论上应该是i只进行了+1,第二次++是一个临时量的自增。这与内置类型发生矛盾,内置类型不支持i(++)++。 自定义的类型重载,应该与内置类型保持一致。
((3.还有一点是,两者在效率上的区别?
后置操作,每次都构造临时对象并返回,会产生临时对象的构造和析构,而且后置中利用前置操作实现。 所以,我们在很多代码中看到,前置++/--的影子,因为前置操作的效率更高。
阅读全文
0 0
- i++和++i的那些陷阱坑
- 关于 i++ 和 ++i i-- 和 --i 的那些事儿
- java i++的陷阱
- ++i,i++,--i和i--的区分
- JAVA中i++的陷阱
- sed -i 的使用陷阱
- i++和++i ,i--和--i的使用方法和作用
- i++和++i, i--和--i的区别
- i++和++i的区别
- i++和++i的问题
- i++ 和 ++i 的问题
- i++和i++的用法
- "i++"和"++i"的区别
- ++i 和 i++ 的区别
- i++和++i的区别
- i++和++i的区别
- i++和++i的区别
- i++和++i的区别
- 创建student.txt
- 数据结构实验之图论一:基于邻接矩阵的广度优先搜索遍历
- 大数据架构:flume-ng+Kafka+Storm+HDFS 实时系统组合
- 函数的三种角色
- PHP增删改查完成感想
- i++和++i的那些陷阱坑
- 51nod 1307 绳子与重物 二分+dfs
- C++调python
- 哈夫曼树(数据结构)
- C++ 支持中文标识符?
- 51nod 1010 只包含因子2 3 5的数
- 一个经典例子让你彻彻底底理解java回调机制
- Kafka 的优化及在商业平台中的应用
- storm初识