最近有个刚学编程的小妹妹问我关于*p++(p为指针)的运算过程,这两操作符优先级一样,且从右到左
运 行。大都初学者会这样认为,p指针先执行++操作,然后再执行*操作。可是结果返回的却是p指针的一开
始 指向的值,而并不是++后指向的值。
这就涉及到前++和后++一些运算的逻辑了。
下面我将通过汇编代码来简单的讲下它们的区别。
下面是c代码:
并附上汇编代码:
我们从int m = 3开始分析,大家可以看到在汇编中,
通过 mov 操作把 3传入dword prt [m]中,
对于m++和++m,它们的指令是一样的,都是先把内存 dword ptr [m]中的值传入eax寄存器,然后进
行自增操作,最后传回内存 dword ptr [m] 中。
下面我们主要讲下 x = (++m)+(++m)+(++m);和y = (m++)+(m++)+(m++);
1. x = (++m)+(++m)+(++m);代码中此时m = 5;
对于大多数初学者来说,或者刚认识前++和后++来的人说,会觉得x = 6 + 7 + 8= 21
可是事实却非如此。看汇编源码:
00E313B7 mov eax,dword ptr [m] //把内存 dword ptr [m]的值(5)传入 寄存器eax中
00E313BA add eax,1 //寄存器自增1
00E313BD mov dword ptr [m],eax //在把寄存器eax中的值(6)传回内存 dword ptr [m]中
00E313C0 mov ecx,dword ptr [m] // 把内存 dword ptr [m]的值(6)传入 寄存器ecx中
00E313C3 add ecx,1 //寄存器自增1
00E313C6 mov dword ptr [m],ecx //在把寄存器ecx中的值(7)传回内存 dword ptr [m]中
00E313C9 mov edx,dword ptr [m] // 把内存 dword ptr [m]的值(8)传入 寄存器edx中
00E313CC add edx,1 //寄存器自增1
00E313CF mov dword ptr [m],edx //在把寄存器ecx中的值(8)传回内存 dword ptr [m]中
首先我们的编译器用3个寄存器(eax,ecx,edx)来计算m的三次自增,而不是初学者想的先计算第一
个++m,然后算出括号中的值为4.
接着编译器 再执行3个+操作 ,汇编代码如下:
00E313D2 mov eax,dword ptr [m] // 把内存 dword ptr [m]的值(8)传入 寄存器eax中
00E313D5 add eax,dword ptr [m] // 把内存 dword ptr [m]的值(8)与 寄存器eax中的值相加
结果保存在eax中则为(16)
00E313D8 add eax,dword ptr [m] // 把内存 dword ptr [m]的值(8)与 寄存器eax中的值相加
结果保存在eax中则为(24)
00E313DB mov dword ptr [x],eax //在把寄存器eax中的值(24)传回内存 dword ptr [m]中
因此x = 8 + 8 + 8 = 24;
2. y = (m++)+(m++)+(m++); 代码中此时m = 8;
对于这行代码,大都初学者会觉得y = 8 + 9 + 10 = 27;
可是结果也让大家大跌眼镜,看汇编代码:
00E313FD mov eax,dword ptr [m] / / 把内存 dword ptr [m]的值(8)传入 寄存器eax中
00E31400 add eax,dword ptr [m] / / 把内存 dword ptr [m]的值(8)与 寄存器eax相加的(16)
00E31403 add eax,dword ptr [m] / / 把内存 dword ptr [m]的值(8)与 寄存器eax相加的(24)
00E31406 mov dword ptr [y],eax //把寄存器eax的值(24)传给内存 dword ptr [y]
从这可以看出,编译器是先进行+操作,然后再运算下面那3个++操作
00E31409 mov ecx,dword ptr [m]
00E3140C add ecx,1
00E3140F mov dword ptr [m],ecx
00E31412 mov edx,dword ptr [m]
00E31415 add edx,1
00E31418 mov dword ptr [m],edx
00E3141B mov eax,dword ptr [m]
00E3141E add eax,1
00E31421 mov dword ptr [m],eax
以上测试只是在VC上做的操作,可能初学者可能看到过为什么不同编译器会出现不同的结果呢,这个
就是得看编译器怎样处理这种像连续对一个变量进行++操作了。
以上测试并不是来说明这种类似的语句就是这样的运算方式,只是对于感兴趣的人看看这些代码在VC
上是如何运行的,一般来说,我们是静止写出这种代码的,因为这种代码对编译器依赖性比较大。不同的
编译器有不同的解析方法。
接下来我来谈谈小妹妹的问题,对于*p++和*++p编译器是如何操作的,
c源码如下:
汇编代码:
我们从int a[] = {1,2,3,4,5};开始
001913A8 mov dword ptr [ebp-1Ch],1 //a
001913AF mov dword ptr [ebp-18h],2 //a+1
001913B6 mov dword ptr [ebp-14h],3 //a+2
001913BD mov dword ptr [ebp-10h],4 //a+3
001913C4 mov dword ptr [ebp-0Ch],5 //a+4
接着
int *p = a;
002113CB lea eax,[ebp-1Ch] //把[ebp-1Ch]的偏移地址取出来,放入eax
002113CE mov dword ptr [ebp-28h],eax //[ebp-28h]是int *p语句产生的开辟的一个额外的
内存,它保存的是 [ebp-1Ch]的偏移地址,也就是a 的偏移地址。
int b = *p++;
002113D1 mov eax,dword ptr [ebp-28h] //先把内存 dword ptr [ebp-28h]保存的值也就是a
的偏移地 址传入eax寄存器中
002113D4 mov ecx,dword ptr [eax] //通过dword ptr [eax] 操作把eax保存的值也就是a的偏移地址从新当做内存的地址,并通过这个偏移地址把该内存中的值(也就是a[0])取出来传入ecx寄存器
002113D6 mov dword ptr [ebp-34h],ecx //把ecx的值(a[0])传入内存 dword ptr [ebp-34h]
// dword ptr [ebp-34h]为变量b的内存
002113D9 mov edx,dword ptr [ebp-28h] //把内存 dword ptr [ebp-28h]保存的值(a的偏移地
址) 传入edx寄存器中
002113DC add edx,4 //因为指针的是由4个字节组成的,所以+4,就等于指
针自增1.此时edx的数据就是a[1]的偏移地址了。
002113DF mov dword ptr [ebp-28h],edx //把edx的值传入内存 dword ptr [ebp-28h]中,及p
指针现在指向a[1]的地址。
对于
p = a;
002113FD lea eax,[ebp-1Ch]
00211400 mov dword ptr [ebp-28h],eax
b = *++p;
00211403 mov eax,dword ptr [ebp-28h]
00211406 add eax,4
00211409 mov dword ptr [ebp-28h],eax
0021140C mov ecx,dword ptr [ebp-28h]
0021140F mov edx,dword ptr [ecx]
00211411 mov dword ptr [ebp-34h],edx
基本差不多的汇编语句,不同的是先把p指针自增,然后取出p指针指向的值。再把该值赋给b.
以上仅对前++和后++的操作给出汇编一些运算逻辑。。希望给感兴趣的人能提供点帮助,如果有表
达不清楚,或者不好的地方。。欢迎大家指出。。交流才会让大家提高更快。。。。