i++与++i是一样的吗?

本文详细探讨了C语言中前置++i与后置i++的区别,通过具体的示例代码和汇编级分析揭示了两者在实际操作中的不同之处。了解这些差异对于正确使用自增运算符至关重要。

  在c语言中,我们经常使用i++++i,可能都会以为它们是一样的,就是对i进行了加1操作。实际上,对于编译器来说它们又意味着什么?会有何不同呢?

之前我都是认为:i++++i都是对i进行了加1操作,唯一有区别的是:

i++ :先取i的值,再对i加1

++i :先对i加1,再取i的值

但是仅仅是这些不同吗?通过下面的一段示例代码,你就会急迫地想知道:编译器到底是如何处理i++++i:

int main()
{
	int i = 0;
	printf("%d,%d,%d\n",i++,--i,i++);
	return 0;
}

如果仅仅按照我们刚才的分析:

在上面的代码中,进行i++操作,先获取i的值0再使得i变为1;再对i进行--i操作,此时i又变为0;最后再i++操作,又是获取到i的值0,再进行+1操作变为1。最终,打印的出的结果一定是:0,0,0


可是程序运行出的结果并不是这样的,显然,编译器并不是像我们之前认为的那样进行处理的,那么这个过程到底发生了什么?

        为了更好的理解这个过程,我们先来看两个简单的i++++i的代码,了解背后所隐藏的细节:

1、编译器中的i++

int main()
{
	int i = 0;
	printf("%d\n",i++);
	return 0;
}

这个很简单,我们知道打印出的结果是:0,


那编译器做了什么,我们通过汇编代码来分析一下:


从汇编代码我们可以看到如下图所示的过程


这个过程通过5步完成,最后传给printf的参数是从临时量中的值。因此,我们认识到:在执行i++时,

(1)编译器向从i的内存空间中把读取到的值传给一个寄存器,寄存器又把值传给一个临时量;

(2)接着又把i内存中的值传给另一个寄存器,然后通过这个寄存器进行+1操作,再把结果传给i的内存中,此时i内存中的值已经变为1.

(3)最后在传参数给printf函数时,传递的是临时量中的值,而不是i内存中的值;

2、编译器中的++i

int main()
{
	int i = 0;
	printf("%d\n",++i);
	return 0;
}

这个很简单,我们知道打印出的结果是:1,


那编译器做了什么,我们通过汇编代码来分析一下:


从汇编代码我们可以看到如下图所示的过程:

这个过程通过4步完成,最后传给printf的参数相当于是从i的内存中获取的。因此,我们认识到:在执行++i时,

(1)编译器向从i的内存空间中把读取到的值传给一个寄存器(eax),寄存器完成对i的+1操作,再把结果写回i的内存;

(2)最后传递给printf的参数值是通过一个寄存器把i内存中的值传递过去的;


小结:看到这里,可以会发现,原来编译器对i++++i的处理过程是有所不同的:虽然最终的结果都是将i内存中的值完成了+1操作,但是i++的处理过程中有临时量的参数,并且传递给printf函数的参数是从临时量中获取的,而不是i的内存;而对++i的处理中没有临时量的产生,是直接通过寄存器完成,最后传递给给printf函数的参数是从i的内存中回去的。

通过上面两个简单的函数,让我们知道了编译器对i++++i的处理过程中的不同,此时我们再回过头看最开始让我们有点疑惑的示例代码,来进一步分析整个过程:

	int i = 0;
008A13DE  mov         dword ptr [i],0  
	printf("%d,%d,%d\n",i++,--i,i++);
008A13E5  mov         eax,dword ptr [i]  
008A13E8  mov         dword ptr [ebp-0D0h],eax  
008A13EE  mov         ecx,dword ptr [i]  
008A13F1  add         ecx,1  
008A13F4  mov         dword ptr [i],ecx  
008A13F7  mov         edx,dword ptr [i]  
008A13FA  sub         edx,1  
008A13FD  mov         dword ptr [i],edx  
008A1400  mov         eax,dword ptr [i]  
008A1403  mov         dword ptr [ebp-0D4h],eax  
008A1409  mov         ecx,dword ptr [i]  
008A140C  add         ecx,1  
008A140F  mov         dword ptr [i],ecx  
008A1412  mov         esi,esp  
008A1414  mov         edx,dword ptr [ebp-0D0h]  
008A141A  push        edx  
008A141B  mov         eax,dword ptr [i]  
008A141E  push        eax  
008A141F  mov         ecx,dword ptr [ebp-0D4h]  
008A1425  push        ecx  
008A1426  push        8A5858h  

有了上面的基础,我们就会从这段汇编代码中发现:

(当然对于printf函数也是一样的,参数的传递是从右向左传的)

编译器对程序中printf("%d,%d,%d\n",i++,--i,i++);一行代码做了大量的操作:可以看到压参操作的指令在比较靠后的位置,所以我们应该知道对于printf函数中i++,--i,i++这3个参数,编译器先完成相应的所有操作,再进行3个参数的压参操作的

根据我们上面的结论,分析这个过程大致为:

(1)i++:(产生临时量a [保存的值为0])完成了对i的+1操作,同时将i内存中的值变为1;

(2)--i :不产生临时量,完成对i的-1操作,将将i内存中的值变为0;

(3)i++:(产生临时量b [保存的值为0])完成了对i的+1操作,同时将i内存中的值变为1;

最终,将i内存中的值变为1;接下来进行压参操作

        i++都有相应的临时量,编译器是将之前临时量中保存的值传给printf函数的

        --i 没有相应的临时量,编译器是去取此时i内存中的值【0】传递给printf函数的

所以最后的结果会是:0,1,0

     总结:后置++(--)与前置++(--)虽然都可以完成对变量的+1(-1)操作,但是处理过程是不同的;

前置++(--)会有临时量的产生,而后置++(--)没有临时量的产生;所以如果后置/后置++(--)涉及作为函数参数时,特别是有多次操作时,我们一定要考虑清楚,否则结果可能出乎意料!




评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值