【c语言】函数调用栈

我们常常会使用函数调用来实现某种操作,只知道从调用函数跳到被调函数,被调函数执行完又返回到主调函数,可能会有以下的疑问:                                                                                                                                                                                                                1.形参开辟空间吗?
2.参数由谁来开辟空间
3.形参的入栈顺序
4.返回值由谁传出来
5.怎么回到调用函数
6.如何知道下一行指令

为了解决以上的问题,以一段简单的代码为例,以下是代码的反汇编:

调用函数:

 

mov    指移动数值
lea  指移动地址
ebp  指向栈底指针
esp  指向栈顶指针
eax ebx ecx edx  都是存储数据的寄存器

 

先从这部分汇编指令开始,

    int rt = 0;
0123141E  mov         dword ptr [rt],0  
    int a = 10;
01231425  mov         dword ptr [a],0Ah  
    int b = 20;
0123142C  mov         dword ptr [b],14h  
这三个汇编指令都是对变量进行初始化,如 Int rt = 0;是根据栈底指针的偏移量确定存储0这个数字的位置

0123141E  mov         dword ptr [rt],0  
0123141E 是指令的虚拟地址,
mov是对数值的移动,
dword ptr [rt]  实际上是dword ptr [ebp-4](栈底指针的偏移量)
简单来讲,意思就是将数值0移动到为ebp-4的地址;int a = 10;与int b = 20亦如此入栈

rt = Sum(a,b);
01231433  mov         eax,dword ptr [b]  
01231436  push        eax  
01231437  mov         ecx,dword ptr [a]  
0123143A  push        ecx  
0123143B  call        Sum (012311C7h)  
01231440  add         esp,8  
01231443  mov         dword ptr [rt],eax  

将b的值(ptr[ebp-12])保存在eax寄存器中,然后压栈,将a的值(ptr[ebp-8])保存在ecx的寄存器中,将ecx压栈;我们发现这里的a和b也就是形参,可知,参数是在主调函数中开辟空间的

call:静态相对位移,call是跳转到被调函数的指令地址包含以下两个步骤:
1.将下一条指令压栈
2.跳转到被调函数;Sum(012311C7h)括号里就是被调函数的指令地址

    被调函数

     

会注意到被调函数中的前面一部分和主调函数中的除了个别数据外,基本一致;其实每个函数前面都会这么一个部分,现在来看这些指令到底是做什么的;

012313D0  push        ebp  
012313D1  mov         ebp,esp  
012313D3  sub         esp,0C0h  
将主调函数的栈底指针的值压栈,然后将ebp移动到esp的位置;sub意思是让esp减等0c0,也就是为新栈开辟了192个字节;这三步主要就是先将主调函数的栈顶指针入栈,然后为新栈开辟空间;

012313D9  push        ebx  
012313DA  push        esi  
012313DB  push        edi  
分别将寄存器ebx、esi、edi依次入栈

012313DC  lea         edi,[ebp-0C0h]  
使edi指向ebp-0C0h的位置;

012313E2  mov         ecx,30h  
012313E7  mov         eax,0CCCCCCCCh  
012313EC  rep stos    dword ptr es:[edi]  
 这三行指令的意思就是循环30h十进制是48次(48*4就是新栈开辟的空间大小),从栈顶到栈底将数据赋值为0xcccccccch

012313EE  mov         eax,dword ptr [a]  
012313F1  add         eax,dword ptr [b] 

在ptr[ebp+8]中将a的值放在寄存器eax中,add是寄存器eax等于eax中的值加上ptr[ebp+c]中的值,也就是计算a+b的值,将结果放在eax中,所以结果是由寄存器带出来的;如果返回值是小于等于四个字节,则由一个寄存器带出来,如果大于4小于8的字节,由两个字节带出来,大于8个字节时,在调用方开辟一个返回值临时量的空间,将return回来的值循环拷贝在临时量区;

销毁栈

013613F4  pop         edi  
013613F5  pop         esi  
013613F6  pop         ebx  
013613F7  mov         esp,ebp  
013613F9  pop         ebp  
先将三个寄存器出栈,将esp移动到ebp,这时esp和ebp都是指向新栈的栈底指针,最后将ebp出栈,ebp就再次指向主调函数的栈底;

回到主调函数中                                                                                                                                                                                                       01361440  add         esp,8  
01361443  mov         dword ptr [rt],eax                                                                                                                                                                 esp指向的还是新栈的栈底,所以add就是将esp向下移动两个字节,指向主调函数的栈顶                                                                         将寄存器eax的值赋给rt(ptr[ebp-4])的位置

 

整个过程就是这个样子了,再回到开始的几个问题,在这个过程中已经解决了
1.形参开辟空间吗?形参开辟空间
2.参数由谁来开辟空间?参数由主调函数开辟
3.形参的入栈顺序?由右向左
4.返回值由谁传出来?由寄存器传出来
5.怎么回到调用函数?将主调函数的栈底指针的值入栈到被调函数中,被调函数的栈底指针释放后,回到主调函数中
6.如何知道下一行指令?call指令的作用就是将下一行指令地址入栈

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值