C语言重中之重,栈帧以及函数的调用过程。

首先需要知道CPU所做的事就是不停地读取程序,分析程序,执行程序。。。还有我们对内存的操作都是以字节为单位。

既然读取程序是从内存上读取的

那么也要知道内存是怎么分配的,按照什么原则分配。这里先对内存做个初步介绍。

      用一张图解释:



       

        其中堆栈相对而生,堆区向上增长,栈区向下增长,栈区内存用完即销,堆区需要程序员自己申请并自己释放,若不释放则会造成内存泄漏,或者程序结束时自动释放(这就是为什么有时候需要重启程序或机器的原因)。

【什么是栈帧结构?】

        我的理解中栈帧结构就是在程序执行期间,在内存中的栈区开辟和释放的空间。一个程序中每个函数在被调用之时都会有自己的栈帧结构,当被调用完之后就会被释放,这个栈帧结构由CPU中的EBP(栈底)和ESP(栈顶)寄存器来维护。

【CPU中存在一些寄存器】

 EAP,EBP,ECP,EXP这些都是通用寄存器

 EBP:栈底寄存器        ESP:栈顶寄存器

 EIP,也称程序计数器,指向当前指令下一条指令的地址:

        在调用一个函数的时候用的是汇编语言中的call命令,call命令的作用是保存当前指令的下一条指令的地址,可能有人会问为什么要保存?那我想问问你出去玩完回不回家?他又问为什么保存的是下一条指令的地址?难道你刚回家就又要出去干同样一件事吗?保存在哪里请继续往下看。

【下面用一个简单的程序来完整了解一下程序的调用过程】
#include<stdio.h>
int ADD(int x,int y)
{
     int z=0;
     z=x+y;
     return z;
}
int main()
{
     int a=10;
     int b=20;
     int ret=ADD(a,b);
     printf("%d\n",ret);
     return 0;
}

【在以往,我们都错误地以为main函数是入口函数】

 那么我们调用堆栈看看:

很明显,在main函数之前还有一个函数叫mainCRTStartup()

由此得出mainCRTStartup函数才是入口函数,而main函数只不过是被它调用的。

由于main函数实际上是被调用的所以它就有自己的栈帧结构。


【进入单步调试,仔细研究函数的每一个过程】
注:随时打开寄存器和内存注意观察。

查看程序的反汇编,按F11进入main函数,边按F11边观察寄存器和内存的变化,在调用函数之前暂停一会,

观察寄存器和内存:

找到指向栈顶和栈底的寄存器,再在内存中找到对应区间进行观察


观查汇编程序


【下面仔细观察形参实例化环节】

先是形参实例化的过程,仔细观察

      

在上面先是将a=10放到了ebp-4的内存上,然后给ebp-8这块内存起个名字叫b,并且其内容为10

在这里却先是ebp-8,然后才是ebp-4。

原因很简单,形参实例化的顺序是从右往左的。

【call命令】

按一下F11

    汇编:

    寄存器:

再按一下

    汇编:

    寄存器:

再按

  

从上面图中可以发现,执行到call命令的时候并不是一步就进入被调函数中,而是先保存当前命令的下一条指令的地址,然后再修改eip到jmp,再修改eip才进入到被调函数里面。

【被调函数的栈帧结构形成过程】

        参考上文

【关于返回值的问题】

        既然被调函数有返回值,但是返回值是局部变量,随着函数的调用结束栈帧结构不复存在,局部变量也被销毁,那么返回值是怎么返回到main函数中的?

        


        发现了吗?返回时是本身销毁了,返回的是一份拷贝,在被调函数中计算结果,将结果存入寄存器eax,而return 返回的时候返回的是寄存器中的值,简而言之,返回值是通过寄存器返回的。

【总结】如图


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值