希望是美好的,也许是人间至善,而美好的事物永不消逝。——《肖申克的救赎》


目录

1、什么是函数栈帧?

2、理解函数栈帧能解决什么问题

3、函数栈帧是什么

3.1什么是栈?

3.2认识寄存器和汇编指令

4、函数调用的整个过程

5、main函数的栈帧形成过程

6、自定义函数栈帧形成过程

7、自定义函数返回到主函数


前言:

大家好,我是拳击哥。我们在编写程序时,会自定义一些函数,我们会用它来进行一些功能实现,那它在内存中的样子是什么呢,实参是如何传参给形参的呢?实际上自定义的函数是在栈区创建的一片空间,我们通过一些汇编指令来实现传参回参。今天我给大家带来的博文内容是什么是函数的栈帧,理解函数的栈帧能解决什么问题。并且理解函数栈帧的创建与销毁的过程。下面我就来详细解析。


1、什么是函数栈帧?

我们在编写程序的时候会自定义函数,自定义函数在调用时。会在内存中开辟一道空间那么这个空间就是该函数的栈帧(stack frame)。那么这些空间里面存放函数参数以及函数返回值,临时变量,保存上下文信息。我们来看一个图大致理解一下:


2、理解函数栈帧能解决什么问题

我们在理解函数栈帧后,以下几个问题就理解了:

  • 局部变量的创建过程
  • 为什么局部变量不初始化内容就是随机值
  • 函数调用的时候是如何传参的,顺序是怎样的
  • 函数的形参和实参是怎样实例化的
  • 函数的返回值能带回什么

3、函数栈帧是什么

3.1什么是栈?

栈(stack)是计算语言中最重要的概念之一,我们运行的每一个程序都是用了栈。如果没有栈就没有函数、局部变量、所有的C语言。

栈被定义为一种特殊的容器,用户可以将数据压入栈中(入栈,push),也可以将已经压入栈中的数据弹出(出栈,pop),但是栈这个容器必须遵守一条规则:先入栈的数据后出栈(First In Last Out, FIFO)。就像叠成一叠的书,先叠上去的书在最下面,因此要最后才能取出。

push:压栈,pop:出栈:

那么如果一块函数的栈帧经过push指令后 ,这块函数的栈帧会延申值最后一个push的指令上方。


3.2认识寄存器和汇编指令

1、寄存器

  • eax:通用寄存器,保留临时数据,常用于返回值
  • ebx:通用寄存器,保留临时数据
  • ebp:栈底寄存器
  • esp:栈顶寄存器
  • eip:指令寄存器,保存当前指令的下一条指令的地址

其中ebp,esp是最重要的,这两个寄存器中存放是地址,它们俩是用来维护函数栈帧的。ebp是指向栈底的,esp是指向栈顶的。它们比较重要,如下图所示:


2、汇编指令

  • mov:数据转移指令
  • push:数据入栈,同时esp栈顶寄存器也要发生改变
  • pop:数据弹出至指定位置,同时esp栈顶寄存器也要发生改变
  • sub:减法命令
  • add:加法命令
  • call:函数调用,1. 压入返回地址 2. 转入目标函数
  • jump:通过修改eip,转入目标函数,进行调用
  • ret:恢复返回地址,压入eip,类似pop eip命令

在认识相关的寄存器和汇编指令后,我们来讲解栈帧的创建与销毁


4、函数调用的整个过程

我们来看一组程序:

#include<stdio.h>int Add(int x, int y)
{int z = 0;z=x + y;return z;
}
int main()
{int a = 10;int b = 5;int c = 0;c = Add(a, b);printf("%d",c);return 0;
}

输出结果:15

我们都知道,这个程序是自定义了一个Add函数来求a与b的和。那么这个程序在内存中运行的过程是怎样呢?接下来容我步步讲解:

转入反汇编

注意今天我们是在VS2019环境下来演示,每个编译器。显示的汇编语言有些许差异,但实质上的逻辑的差不多。函数调用的过程和返回的过程都相同,因此不必纠结编译器。

打开反汇编后,我们就看到了一些汇编指令push、mov、sub、lea等等。

注意以下所有的过程都是拿此程序来讲解。


5、main函数的栈帧形成过程

首先我们要知道main函数也是被调用的,是被_tmainCRTStartup调用的。那么main函数形成的过程,就是通过上述汇编指令一步步完成。


1、第一条指令

首先我们来看第一个条指令push:edp和第二条指令mov:ebp,esp。

push:ebp就是在_tmainCRTStartup的栈顶上压一块空间这个存的就是ebp。注意每次压栈过后,栈底指针会指向最上方。

mov:ebp,esp就是使esp的值给ebp,因此ebp和esp指向的地方是同一方向。


2、第三条指令

我们再来看第三条指令sub:esp,0E4h。

sub:esp,0E4h就是让esp减去0E4h,因此 esp指向到了内存中的上一块区域。此时esp和ebp分别指向了新的空间,而这块新的空间就是main函数的栈帧。


3、第四到第六条指令

第四到六条指令push:ebx,push:esi,push:edi。

这三个push分别在main函数栈帧上方压三条指令,压栈完后,此时的栈顶指针也是指向了最高层。


4、第七至第十条指令

第七条至第十条指令,lea:edi,[ebp-24h] ~rep stos    dword ptr es:[edi]

lea的是意思是load effecitve address(加载有效地址),lea:edi,[ebp-24h]意思是把ebp到ebp后24h个位置里面的值都加载成CCCCCCCC。所以不初始化变量输出的是随机值,是因为CCCCCCCC里面存放都是一些类似于烫烫烫烫这样的乱码。

此时main函数栈帧的开辟已经完成了。里面的CCCCCCCC都是可以为main函数所用的空间


5、mov创建局部变量a

mov:dword ptr [ebp-8],0Ah,意思是把0Ah也就是10放到ebp-8的位置。此时局部变量a=10已在main函数栈帧里面创建成功,所在的位置是ebp-8。dword是双倍word,word占两个字节。因此dword占四个字节。

这就是局部变量a=10创建的过程


6、mov创建局部变量b

mov:dword ptr [ebp-14h],5。意思是把5放到 edp-14h(20d)的位置。此时局部变量b=5已经在main函数栈帧创建成功,所在位置为edp-14h。

这就是局部变量b=5的创建过程。


7、mov创建局部变量c

mov:dword ptr[edp-20h]把0赋值给edp-20h的位置。

这就是变量c的创建过程


6、自定义函数栈帧形成过程

函数传参的过程,5中我们通过汇编指令了解到了局部变量在内存里面的创建步骤。下面我们就来看函数调用过程。首先我们得进入Add函数,会出现以下界面:


1、局部变量压栈1

mov:eax,dword ptr [ebp-14h]意思是把ebp-14h里面的值5存到寄存器eax里面

push:eax意思是把eax压在最顶部,也就是b的值5放在了最顶部。

这就是形参b的创建过程


2、局部变量压栈2

mov:eax,dword ptr [ebp-8]意思是把ebp-14h里面的值10存到寄存器ecx里面

push:ecx意思是把eax压在最顶部,也就是a的值10放在了最顶部。


3、call指令

call指令作用是函数调用1. 压入返回地址 2. 转入目标函数,也就是把call下一条指令压入栈顶。方便函数返回时找到来时路。注意每次重新调试时 ,地址都会不一样。但功能是一样的。

4、使用形参

因为函数栈帧创建的过程在main函数栈帧创建中已经讲到了,此时我们省略Add创建过程,直接来到使用形参。

上面我们说到了,ecx和eax这两个寄存器放的是两个局部变量的值,实质上此时的ecx和eax就是形参x和y的值。我们可以让栈底指针依次向下访问这两个寄存器的值(形参的值)这样就可以进行安全的操作。我们常说对形参进行修改不会影响实参,就是这个道理。因为形参是在寄存器里面存储的。


7、自定义函数返回到主函数

当函数返回主函数时会出现以下界面。我们不用管上一部分,我们来看下半部分。

下半部分就是Add函数的销毁过程,分别pop栈底的三个指令,然后通过cmp、mov指令回到main函数主体。


最后main函数回到了最初的样子


最后输出c的值

注意,我们先把z的值放到了寄存器eax里面。 不然Add函数的栈帧结束后z变量里面存的值都会被销毁。

mov:dword ptr[ebp-20h],eax。此时把eax的值放到了ebp-20h的位置。因此c的值就变为了z的值。这就是返回值。


总结:

局部变量创建过程是通过栈底指针ebp来执行的

函数创建后的内存里面默认值是CCCCCCCC因此会造成不初始化变量输出乱码

形参放在是eax和ecx或ebx里面的


本期博客到这里就结束了,相信大家已经理解了开头的理解函数栈帧能解决什么问题。感谢您的观看

Never Give Up

C语言函数调用的过程图解深入剖析相关推荐

  1. c语言中staloc是什么意思,C语言函数调用栈(三)

    6 调用栈实例分析 本节通过代码实例分析函数调用过程中栈帧的布局.形成和消亡. 6.1 栈帧的布局 示例代码如下: //StackReg.c #include //获取函数运行时寄存器%ebp和%es ...

  2. 【C 语言】编译过程 分析 ( 预处理 | 编译 | 汇编 | 链接 | 宏定义 | 条件编译 | 编译器指示字 )

    相关文章链接 : 1.[嵌入式开发]C语言 指针数组 多维数组 2.[嵌入式开发]C语言 命令行参数 函数指针 gdb调试 3.[嵌入式开发]C语言 结构体相关 的 函数 指针 数组 4.[嵌入式开发 ...

  3. C语言程序编译过程 2

    C语言的编译链接过程要把我们编写的一个c程序(源代码)转换成可以在硬件上运行的程序(可执行代码),需要进行编译和链接.编译就是把文本形式源代码翻译为机器语言形式的目标文件的过程.链接是把目标文件.操作 ...

  4. C语言函数调用的原理

    该博文为原创文章,未经博主同意不得转载,如同意转载请注明博文出处 本文章博客地址:https://cplusplus.blog.csdn.net/article/details/105088660 C ...

  5. 揭示C语言函数调用的本质解析

    C语言是面向过程的,而C++是面向对象的C和C++的区别: C是一个结构化语言,它的重点在于算法和数据结构.C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过 ...

  6. C语言C6292错误,测试c语言函数调用性能因素

    标签: 函数调用:即调用函数调用被调用函数,调用函数压栈,被调用函数执行,调用函数出栈,调用函数继续执行的一个看似简单的过程,系统底层却做了大量操作. 操作: 1,               调用函 ...

  7. C语言函数调用栈(一)

    以下全文转载自:C语言函数调用栈(一) 程序的执行过程可看作连续的函数调用.当一个函数执行完毕时,程序要回到调用指令的下一条指令(紧接call指令)处继续执行.函数调用过程通常使用堆栈实现,每个用户态 ...

  8. 红帽Linux 9光盘启动安装过程图解电脑教学

    红帽Linux 9光盘启动安装过程图解 电脑教学 2008-06-12 08:28:15 阅读385 评论0   字号:大中小 订阅 RedHat Linux是目前世界上使用最多的Linux操作系统. ...

  9. 安装redhat系统步骤图解_linux安装教程(红帽RedHat Linux 9)光盘启动安装过程图解

    RedHat Linux是目前世界上使用最多的Linux操作系统.因为它具备最好的图形界面,无论是安装.配置还是使用都十分方便,而且运行稳定,因此不论是新手还是老玩家都对它有很高的评价.现在,RedH ...

最新文章

  1. Space X和NASA到底有什么关系?
  2. 深度解析 H.265 视频解决方案
  3. 颈椎病2句话就能治疗好,你也试试看,一学就会!
  4. 金蝶云如何html5登录,第三方系统单点登录到金蝶云指南V2
  5. 博弈论-囚徒困境与重复囚徒困境的启示
  6. html单击按钮时弹出输入框,点击按钮弹出模态框的一系列操作代码实例
  7. 把执行结果转成json对象报错_JSONObject获取值后为一个对象,将对象转为JSONObject时报错...
  8. Unity SRP自定义渲染管线 -- 5.Directional Shadows
  9. 查询去除空值_SQL数据处理(五):SQL多表查询
  10. 牛客练习赛 栈和排序
  11. LaTeX学习笔记(legacy)~
  12. 中国基因工程行业市场供需与战略研究报告
  13. 自由软件基金会官宣Zoë Kooyman担任新执行董事
  14. BDS Business Development Studio
  15. android - 房源登记模版
  16. Git本地仓库的文件夹不显示红色感叹号、绿色对号等图标
  17. 不知道图片加文字水印怎么弄?这3个方法自媒体达人必学
  18. 情人节程序员用HTML网页表白【粉色烂漫的七夕情人节专题页面】 HTML5七夕情人节表白网页源码 HTML+CSS+JavaScript
  19. 脑部神经系统结构模式图,大脑的神经结构示意图
  20. 火焰焰心matlab,火焰的形貌-中性焰、碳化焰、氧化焰

热门文章

  1. 一直想找个老师学习Photoshop,今天终于找到了!!!
  2. WordPress多功能新闻积分商城主题LensNews (免费分享)
  3. 从零单排JavaScript第一期
  4. vue项目启动webpack打包sass报错
  5. 【uni-app】搜索框的防抖处理
  6. (转)Python网络爬虫实战:世纪佳缘爬取近6万条数据
  7. 全球名校课程作业分享系列(10)--斯坦福CS231n之Network visualization
  8. MotionBuilder-动作整合-Story(一)
  9. 【Gazebo入门教程】第二讲 模型库导入与可视化机器人建模(模型编辑器)
  10. python练习 7-14 漂亮的螺旋 (100分)