c++ swap函数_函数和栈(一)

在内存中是一块特殊的存储空间,它的存储原则是“先进后出”,即最先存储的数据最后被释放。在汇编过程通常使用PUSH和POP指令对栈空间执行数据的压入和弹出的操作。栈是从高地址往低地址生长的。

当调用一个函数时,就会针对调用的函数开辟出所需的栈空间,从而形成函数的栈帧。在VC++中,栈帧中可以寻址的数据有局部变量、函数返回地址、函数参数等。当这个函数结束调用时,需要清除掉它所使用的栈空间,关闭栈帧,我们称这一过程为栈平衡。如果开辟的栈空间没有被正确关闭,那么将会造成栈空间的上溢或下溢,造成程序的奔溃。

1.下面来看一个例子:

#include void swap(int *, int *);int main(){int a=10;int b=20;printf("交换前 a= %d, b= %d ",a,b);swap(&a,&b);printf("交换后 a= %d, b= %d",a,b);getchar();return 0;}void swap(int *a, int *b){int temp;temp = *a;*a = *b;*b = temp;}

反汇编(debug版本)后为:

下面是main的入口初始化,进入函数后,先保存原来的ebp,”push ebp”。 然后调整ebp的位置到esp,为”mov ebp,esp”。接着通过”sub esp,4ch”来开辟0x4ch的栈空间,这是留给局部变量使用的。还有 “ push ebx”,” push esi”,” push edi” 是用来保存寄存器现场。

00401020 push ebp00401021 mov ebp,esp00401023 sub esp,4Ch00401026 push ebx00401027 push esi00401028 push edi........

在函数调用结束后,需要将之前开辟的栈空间释放掉,此时,需要先“ pop edi”,” pop esi”,” pop ebx”来恢复寄存器现场。因为栈是先进后出的结构,所以先弹出最后压入的值。然后到”add esp,4ch”意思是回收开辟的栈空间。接着”mov esp,ebp” 意思是还原esp指针的值,再接着”pop ebp” 还原 ebp 原来的值,最后 “ret” 指令是弹出函数之前压入的返回地址。到此,一个函数的调用就结束了。(其中 cmp ebp,esp call __chkesp 为debug版本特有的)

............004010C9 pop edi004010CA pop esi004010CB pop ebx004010CC add esp,4Ch004010CF cmp ebp,esp004010D1 call __chkesp (00401510)004010D6 mov esp,ebp004010D8 pop ebp004010D9 ret

下面来看看Swap函数的调用过程。

20: void swap(int *a, int *b)21: {00401110 push ebp00401111 mov ebp,esp00401113 sub esp,44h00401116 push ebx00401117 push esi00401118 push edi00401119 lea edi,[ebp-44h]0040111C mov ecx,11h00401121 mov eax,0CCCCCCCCh00401126 rep stos dword ptr [edi]22: int temp;23: temp = *a;00401128 mov eax,dword ptr [ebp+8]0040112B mov ecx,dword ptr [eax]0040112D mov dword ptr [ebp-4],ecx24: *a = *b;00401130 mov edx,dword ptr [ebp+8]00401133 mov eax,dword ptr [ebp+0Ch]00401136 mov ecx,dword ptr [eax]00401138 mov dword ptr [edx],ecx25: *b = temp;0040113A mov edx,dword ptr [ebp+0Ch]0040113D mov eax,dword ptr [ebp-4]00401140 mov dword ptr [edx],eax26: }00401142 pop edi00401143 pop esi00401144 pop ebx00401145 mov esp,ebp00401147 pop ebp;ret 指令 是 弹出返回地址,由于是__cdecl 调用约定,所以函数压入的参数(两个参数) 由调用者(main函数)清理。( 如 下图所示)00401148 ret
5b4d865bb51f55e1fd1c27e96725335b.png

2.下面是一个用汇编写的同样功能的例子

.386.model flat,stdcall ;__stdcall 调用约定option casemap :noneINCLUDE windows.incINCLUDE kernel32.incINCLUDELIB kernel32.libincludelib msvcrt.lib ;c运行库Swap PROTO,pV1:PTR DWORD,pV2:PTR DWORD ;函数声明getchar PROTO C:DWORDprintf PROTO C:DWORD,:VARARG.datav1 DWORD 10v2 DWORd 20old byte "交换前 a= %d, b= %d",0dh,0ah,0new byte "交换后 a= %d, b= %d",0dh,0ah,0.codemain procINVOKE printf,ADDr old,v1,v2INVOKE Swap,ADDR v1,ADDR v2 ;调用swap 函数INVOKE printf,ADDr new,v1,v2INVOKE getchar,NULLINVOKE ExitProcess,NULLmain endpSwap PROC USES ebp esi edi ebx edx,pV1:PTR DWORD,pV2:PTR DWORDmov esi,pV1mov edi,pV2mov ebx,[esi] ;temp = *a;mov edx,[edi] ;mov [esi],edx ; *a = *b;mov [edi],ebx ; *b = temp;ret ;返回函数地址和清理函数参数Swap ENDPend main

Swap函数 反汇编为:

00401052 /$ 55 push ebp00401053 |. 8BEC mov ebp, esp00401055 |. 55 push ebp00401056 |. 56 push esi00401057 |. 57 push edi00401058 |. 53 push ebx00401059 |. 52 push edx0040105A |. 8B75 08 mov esi, dword ptr [ebp+8]0040105D |. 8B7D 0C mov edi, dword ptr [ebp+C]00401060 |. 8B1E mov ebx, dword ptr [esi]00401062 |. 8B17 mov edx, dword ptr [edi]00401064 |. 8916 mov dword ptr [esi], edx00401066 |. 891F mov dword ptr [edi], ebx00401068 |. 5A pop edx00401069 |. 5B pop ebx0040106A |. 5F pop edi0040106B |. 5E pop esi0040106C |. 5D pop ebp;leave指令等同于 mov esp,ebp 和pop ebp0040106D |. C9 leave;ret 指令 相当于 弹出返回地址 和add esp,8(由于函数是__stdcall 调用,所以由该函数自己清理)0040106E . C2 0800 retn 8
29af78528785204c3009cb0356f00922.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值