c语言:通过一个例子来认识函数栈帧的创建和销毁讲解

简介: c语言:通过一个例子来认识函数栈帧的创建和销毁讲解

我们利用一个简单的c语言函数来认识函数栈帧的创建和销毁讲解。

  • 函数栈帧的创建和销毁讲解

例:

a.寄存器分为eax,ebx,ecx,edx,以及ebp,和esp


其中ebp和esp这两个寄存器中存放的是地址


这两个地址是用来维护函数栈帧的。


b.每个函数调用,都会在栈区创建一个空间


有两个寄存器esp和ebp记住了两个地方的地址,在调用哪一个函数,ebp和esp就是在维护哪一个函数的函数的函数栈帧(无论是库函数还是自己创造的函数)

main函数也是被其他函数(__tmainCRtartup)调用的,然后__tmainCRtartup这个函数也是被mainCRtartup这个函数调用的。

c.调用总过程大概是这样的

调用总过程的具体是这样

先进行push(压栈)放入一个ebp在上面,然后esp是栈顶指针,esp向上走一步,然后因为上面是低地址,所以esp减小了。

就像下面这样

然后是mov操作,将esp的地址给ebp。

然后进行了sub操作,就是将前面的减去后面的,如下图:就是esp-0E4h,这样esp就上移到了某一个特殊的地方了吧

这样esp和ebp形成了新的范围,创建出来main函数的空间啦。

然后再进行三次push(压栈),分别压进去ebx,esi,edi这三个值,然后esp也先上移动了

然后就进行了lea操作了,就是将(ebp-0E4h)这个地址赋给edi,因为三次push之前的esp的地址就是(ebp-0E4h),

所以其实就是将三次push前的esp的地址赋给了edi了。

然后下面不是有两个mov操作嘛,第一个mov是将39h这么多个数据赋给了eca,然后第二个mov操作是将0ccccccch赋给了eax。然后rep stos这个创造是将edi以下ecx(39h)这么多个数据全都附上eax(0ccccccch)这个值。

这样就将main这个函数的空间创建好了。

再下来就是进入main括号里面的内容了,开始定义东西了。

将0A(16进制就是10)赋到ebp-8的地址

再将14h赋到ebp-14h这个地址,再将0赋给ebp-20h这个地址。

然后再下来,mov将ebp-14h的值赋给了eax,再将eax进行压栈,然后同样的道理,将ecx(ebp-)8压栈。

接下来就是执行call指令了,将call的下一条指令的目的地的地址进行压栈,并且在call的下一条指令做一个标志,后面刚好可以回来call指令的下一条指令。

然后就到了了add函数啦


然后又进行了push等系列的操作,开辟了add函数的空间。


而之前的ebp和esp也上移了


而其中x和与y是由a和b传值过去的(add(a,b)这个是由右向左传的,也就是b先传),第一个mov将ebp+8赋到了eax,然后进行add操作将ebp+0ch(这个是ebp+12)与eax相加


,然后将相加的结果赋给eax,然后再进行一个mov操作,将eax的值赋给ebp-8。


注:像这里形参不是在这个创建的,而是找到了传参的时候那个值的空间。这里的形参只是实参的一次拷贝,所以形参不会改变实参的值。


然后现在把z算出来了。

然后又进行进行了一次mov操作,将ebp-8赋给了eax。


注:注意到本例子是在add函数内部创建了变量z,那为啥这里临时变量z可以将值传过去main函数呢?(临时变量在函数使用完成后就会销毁。)


因为它现将ebp-8赋给eax,而eax是一个寄存器,寄存器是不会因为函数结束了,就迅速销毁的,所以可以将值传过去。


然后就要进行pop操作了,pop操作就是将edi等销毁(弹出)的,而esp向下移动了

然后mov,将ebp的地址赋给了esp,这样就将add的空间销毁了。

然后进行pop,将ebp这个销毁了,同时esp下移了,ebp也回到了main里面了,找到了main的ebp和esp。

进行ret操作后,就又跳了call指令的下一条指令的位置。

然后将进行add操作和mov操作,将esp+8来导致esp下移,这个add操作会导致esp+8和esp+12这两个形参销毁。

注:所以说形参什么时候销毁呢?

形参在这个add操作后销毁了,没了。

然后进行mov操作,将eax赋给ebp-20h,这里就实现了返回值带回来了。

同理,main函数的return也是这样返回了,就不多做解释。

相关文章
|
6月前
|
存储 安全 C语言
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-2
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-2
|
6月前
|
存储 编译器 C语言
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-1
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-1
|
6月前
|
存储 编译器 程序员
C语言之反汇编查看函数栈帧的创建与销毁(一)
C语言之反汇编查看函数栈帧的创建与销毁(一)
C语言之反汇编查看函数栈帧的创建与销毁(一)
|
3月前
|
存储 C语言
【C语言】——函数栈帧的创建与销毁
【C语言】——函数栈帧的创建与销毁
|
6月前
|
存储 编译器 C语言
C语言:底层剖析——函数栈帧的创建和销毁
C语言:底层剖析——函数栈帧的创建和销毁
|
6月前
|
存储 编译器 程序员
C语言之反汇编查看函数栈帧的创建与销毁(二)
C语言之反汇编查看函数栈帧的创建与销毁(二)
|
11月前
|
编译器 C语言
函数栈帧的创建和销毁(以C语言代码为例,汇编代码的角度分析)(下)
函数栈帧的创建和销毁(以C语言代码为例,汇编代码的角度分析)
|
1月前
|
C语言 C++
C语言 之 内存函数
C语言 之 内存函数
35 3
|
14天前
|
C语言
c语言调用的函数的声明
被调用的函数的声明: 一个函数调用另一个函数需具备的条件: 首先被调用的函数必须是已经存在的函数,即头文件中存在或已经定义过; 如果使用库函数,一般应该在本文件开头用#include命令将调用有关库函数时在所需要用到的信息“包含”到本文件中。.h文件是头文件所用的后缀。 如果使用用户自己定义的函数,而且该函数与使用它的函数在同一个文件中,一般还应该在主调函数中对被调用的函数做声明。 如果被调用的函数定义出现在主调函数之前可以不必声明。 如果已在所有函数定义之前,在函数的外部已做了函数声明,则在各个主调函数中不必多所调用的函数在做声明
29 6
|
1月前
|
存储 缓存 C语言
【c语言】简单的算术操作符、输入输出函数
本文介绍了C语言中的算术操作符、赋值操作符、单目操作符以及输入输出函数 `printf` 和 `scanf` 的基本用法。算术操作符包括加、减、乘、除和求余,其中除法和求余运算有特殊规则。赋值操作符用于给变量赋值,并支持复合赋值。单目操作符包括自增自减、正负号和强制类型转换。输入输出函数 `printf` 和 `scanf` 用于格式化输入和输出,支持多种占位符和格式控制。通过示例代码详细解释了这些操作符和函数的使用方法。
39 10