函数的堆栈调用


下面通过一个例子来看看函数调用堆栈的过程

#include <stdio.h>
int Sum(int lhs,int rhs)
{
	int tmp=lhs+rhs;
	return tmp;
}

int main()
{
	int a=10;
	int b=20;
	int rt=0;
	rt=Sum(a,b);
	return 0;
}

将上面代码转到反汇编:

main函数

10:   {
0040D780   push        ebp  //将ebp入栈
0040D781   mov         ebp,esp  //将esp指向ebp
0040D783   sub         esp,4Ch //给main函数开辟4ch大的栈帧空间
0040D786   push        ebx  // 将ebx入栈
0040D787   push        esi   // 将esi入栈
0040D788   push        edi  // 将edi入栈
0040D789   lea         edi,[ebp-4Ch]  //将ebp-4Ch放到edi中
0040D78C   mov         ecx,13h  //将13h放到ecx中,ecx是计数器
0040D791   mov         eax,0CCCCCCCCh  //将0CCCCCCCCh放到eax中,eax是累加器
0040D796   rep stos    dword ptr [edi]  //从edi开始,向高地址部分进行字节拷贝,每次拷贝4个字节,拷贝eax的内容,拷贝13h次,上面这几步给开辟的空间初始化
11:       int a=10;
0040D798   mov         dword ptr [ebp-4],0Ah  //将10存入ebp-4的位置
12:       int b=20;
0040D79F   mov         dword ptr [ebp-8],14h  //将20存入ebp-8的位置
13:       int rt=0;
0040D7A6   mov         dword ptr [ebp-0Ch],0  //将0存入ebp-12的位置
14:       rt=Sum(a,b);
0040D7AD   mov         eax,dword ptr [ebp-8]  
0040D7B0   push        eax  //把ebp-8的值赋给eax寄存器,将eax寄存器入栈
0040D7B1   mov         ecx,dword ptr [ebp-4]
0040D7B4   push        ecx   //把ebp-4的值赋给eax寄存器,将ecx寄存器入栈
0040D7B5   call        @ILT+10(Sum) (0040100f)  //将下一条指令的地址入栈,并跳转到Sum函数的栈帧
0040D7BA   add         esp,8
0040D7BD   mov         dword ptr [ebp-0Ch],eax  //将eax的值存入ebp-12

15:       return 0;
0040D7C0   xor         eax,eax
16:   }

 

Sum函数

4:    {
0040D750   push        ebp  //将main函数的ebp入栈
0040D751   mov         ebp,esp  //将esp指向ebp
0040D753   sub         esp,44h //给Sum函数开辟4ch大的栈帧空间 
0040D756   push        ebx  // 将ebx入栈
0040D757   push        esi  // 将esi入栈
0040D758   push        edi  // 将edi入栈
0040D759   lea         edi,[ebp-44h]  //将ebp-44h放到edi中
0040D75C   mov         ecx,11h  //将11h放到ecx中,ecx是计数器
0040D761   mov         eax,0CCCCCCCCh  //将0CCCCCCCCh放到eax中,eax是累加器
0040D766   rep stos    dword ptr [edi]  //从edi开始,向高地址部分进行字节拷贝,每次拷贝4个字节,拷贝eax的内容,拷贝11h次,上面这几步给开辟的空间初始化,和main函数中的过程是一样的
5:        int tmp=lhs+rhs;
0040D768   mov         eax,dword ptr [ebp+8]  //将ebp+8的值给eax
0040D76B   add         eax,dword ptr [ebp+0Ch]  //将ebp+12的值给eax,并与之前eax中的值相加
0040D76E   mov         dword ptr [ebp-4],eax  //将eax的值存入ebp-4
6:        return tmp;
0040D771   mov         eax,dword ptr [ebp-4]  //将ebp-4的值给eax

7:    }
0040D774   pop         edi
0040D775   pop         esi
0040D776   pop         ebx
0040D777   mov         esp,ebp
0040D779   pop         ebp
0040D77A   ret

由上面的过程可以画出如下图:

ebp:栈底指针寄存器,指向当前函数的栈底位置
esp:栈顶指针寄存器,指向当前函数的栈顶位置
pc:下一行指令寄存器,保存下一条指令的物理地址

通过上面的过程,可得出开栈需要以下几个步骤:
1、压入实参,形参初始化;
2、压入下一行指令的地址,当函数调用结束后,继续执行下一行指令;
3、压入调用方栈底地址,被调用方结束后回退到调用方的栈帧;
4、移动ebp到被调用方栈底
5、被调用方开辟活动空间并初始化为cccc cccc
清栈和上面的过程相反。


1、形参开辟内存吗?
形参开辟内存,由调用方开辟。

2、形参和实参的在入栈顺序?
实参按照从左到右的顺序入栈,形参按照从右到左的顺序入栈

3、返回值由谁带出来?
返回值<4个字节,由eax寄存器带出来
4个字节<返回值<8个字节,由eax寄存器和edx寄存器带出来
返回值>8个字节,由临时量带出来,临时量存储在内存上

4、被调用方结束后怎么知道回退到调用方?
被调用方保存了调用方的栈底指针地址

5、函数调用结束后,怎么知道继续执行下一行指令?
被调用方保存了下一行指令的地址,当被调用方清栈时得到这个地址返回调用方的下一行指令的地址,继续执行下一行指令

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值