函数栈帧的创建与销毁

在日常的代码生活中,函数总是与我们形影不离,但是我们是否有想过,函数到底是如何在内存中创建存放的呢?作为一名优秀的码农,我们万万不可知其然而不知其所以然,如果想要更好的掌握理解所学知识,我们对知识应该有一个更深层次理解,所以接下来来了解一下有关函数的创建与销毁问题

在正式开始之前,还需要了解几个知识:

寄存器

常使用的寄存器有很多种,例如:eax  ebx  ecx  edx  ebp  esp等等

今天与内容有关的主要就是 ebp esp,这两个寄存器中存放的是地址

而这两个地址用来维护函数栈帧(类似两个保安看守一个小区)

 内存

而函数以及函数的临时变量(形参)就是存放在栈区 

 部分汇编指令

push----PUSH 指令首先减少 ESP 的值,再将源操作数复制到堆栈。操作数是 16 位的,则 ESP 减 2,操作数是 32 位的,则 ESP 减 4(将对象进行压栈);
mov ----MOV 指令将源操作数复制到目的操作数;
sub  ----两个操作数的相减,即从A中减去B,其结果放在A中;
lea   ----LEA指令将存储器操作数mem的4位16进制偏移地址送到指定的寄存器;
pop ----将操作对象弹出栈帧,出栈
call ----调用函数,调用前会将call下一条语句的地址压栈在栈顶
ret  ----将栈顶的地址弹出并返回到该地址的地方

 ok,了解完毕,正式进入主题


压栈:给栈顶放一个元素(push)

出栈:从栈顶删除一个元素(pop)

在每一个函数运行前,系统都需要为函数以及函数的临时变量在栈区中开辟空间,而这块空间也就是叫做函数的函数栈帧

这就需要寄存器esp和ebp来进行管理了:

ebp存放它的高地址,esp存放它的低地址,这两个地址之间的内存就形成了当前函数的函数栈帧,每当需要调用一个新的函数esp和ebp就会去维护那个新的函数栈帧,两个寄存器里面就会存放两个新的地址

其实,main函数也是一个会被内置函数调用的函数,从调试中我们可以看出

 

 main函数被一个叫做mainCRTStartup的函数所调用,所以我们用画图的方式来表达是这样的

 一开始两个指针指向管理的是mainCRTStartup函数的函数栈帧,该函数调用main函数时,从调制中的汇编指令来看看这两个指针是如何操作的

 

通过压栈的一系列操之后的内存示意图如下:

 但还没完,还有下面的操作

 执行到这,为main函数空间的开辟才正式结束,示意图如下

 继续执行代码指令

 存放变量后如下图所示

变量不初始化打印随机值的原因,也正是因为在开辟函数栈帧是赋值为CCCCCCCCC,在不同的编译器下就会编译出不同的值

执行完变量初始化指令后正式调用Add函数

接下来是传参操作

 示意图如下

 进入Add函数

 示意图如下

到这里就执行完Add函数

接下来是销毁操作

 最终示意图如下

最后main函数栈帧的销毁过程也和add函数的一样,就不再过多介绍了(真的很累画这个图)

下面发散思考几个问题

1.局部变量是怎么创建的?

函数栈帧创建后编译器分配由高到低地址创建变量;

2.为什么局部变量不初始化的值是随机值?

函数栈帧创建后会默认将所有内容初始化为0CCCCCCCCCCh;

3.函数是怎么传参的?传参的顺序是怎么样的?

传参是将实参值拷贝后进行压栈在栈顶,顺序是由右到左;

4.形参和实参是什么关系?

形参是实参的临时拷贝,只是值相同却是不同的地址;

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值