C语言内功的修炼--函数栈帧创建和销毁

简介: C语言内功的修炼--函数栈帧创建和销毁

什么是栈帧

简单地说

程序的执行过程可看作连续的函数调用,而C语言中,每个栈帧对应着一个未运行完的函数


每个函数的每次调用(通常使用堆栈实现),都有它自己独立的一个栈帧


这个栈帧中保存了该函数的返回地址和局部变量维持着所需要的各种信息


所以栈帧也叫过程活动记录,是编译器用来实现过程/函数调用的一种数据结构


  • 从逻辑来看

栈帧就是一个函数执行的环境:函数参数、函数的局部变量、函数执行完后返回到哪里等

什么是栈

  • 在详解之前我们还得明白一点栈:

栈,也叫堆栈,是一种数据结构,具有先进后出的特点(类似子弹上弹夹)

在函数栈帧创建过程中,内存从高地址往低地址使用

20210807153431469.png

寄存器edp存放了指向函数栈帧栈底的地址(高地址)

寄存器esp存放了指向函数栈帧栈顶的地址(低地址) 

esp和ebp共同维护函数栈帧

栈帧的创建与销毁

  • 在VS2013下逐步调试add函数向大家展示并讲解栈帧的创建和销毁过程
int Add(int x, int y)
{
  int z = 0;
  z = x + y;
  return z;   
}
int main()
{
  int a = 10;
  int b = 20;
  int ret = 0;
  ret = Add(a,     b);  
  printf("%d\n", ret)
  return 0;
}


main函数调用过程

  • 转到反汇编

20210807153138109.png

  • main函数的栈帧分配前

20210807153232446.png

main函数是由__tmainCRTStartup函数调用的

对于vs来说__tmainCRTStartup函数也由其他函数调用(取决于编译器


  • main函数的栈帧分配


20210807154325395.png

move(赋值) ebp,esp 意思是把esp的内容给ebp,ebp存了esp里面的内容

就是说ebp指向了main函数的栈底地址

esp减去0E4h大小的值(内存从高地址往低地址使用

即ebp的上面开辟了0E4h这个大小的一个空间(即main函数空间)

esp指向main函数的栈顶地址(esp和ebp开始维护main函数空间


把 39h存到ecx里面,把0cccccccch存到eax这个寄存器中(初始化main函数)

rep stos(重复拷贝),dword就是doubleword(word是两个字节,double就是4个字节)

从这里看来ecx以及eax寄存器起到将相关命令参数存放传递的作用


mov(赋值)命令将[ebp - 8]地址中的内容赋值给eax,并用push(压栈)将eax压入栈顶(相当于将b的值压入了栈顶)同样将[ebp - 4]地址中的内容赋值给ecx,并将ecx压入栈顶

这里也就是在给Add函数传参,那这里被压入栈顶的两个寄存器就相当于a,b的一份临时拷贝


call(声明函数返回地址)(不管是变量还是函数都在内存中存放,但因为内存的分配使得他们的地址并不连贯,所以为了程序执行的流畅,这里需要声明被调用函数执行完成后返回上一级函数的地址)在执行call指令同时,在栈顶ecx又开辟了一开新空间,用于存放call指令下一条指令的地址,便于返回

Add函数的调用过程

  • 汇编代码

20210807154402448.png

  • 栈帧创建和销毁过程

20210807154450458.png

mov(赋值)将[ebp + 8]地址的内容赋值给eax,add(加法)将[ebp + 0Ch]地址的内容加给eax,然后mov(赋值)将eax内容赋值给[ebp - 4]地址的内容

函数内部并没有直接创建一个参数x,y,而是调用了传参过来的寄存器中的值


返回z的值时,mov(赋值)将z的值赋值到了eax中

从这里看来,函数返回值的传递实质上是通过寄存器传递的


函数结束,pop(出栈)指令,将edi, esi,ebx寄存器退出栈顶


并用mov(赋值)命令将ebp寄存器中的地址赋值给esp(ebp,esp是维护空间边界的两个寄存器,当他俩地址相遇时,代表这片空间消失)相当于将为Add函数开辟的空间销毁


再出栈ebp就回到了main函数的栈底


ret(返回)返回存放在当前栈顶的地址中的地址值,即回到了刚才Call(声明返回地址)指令的下一条指令处(即把形参也弹出去了)


add函数栈帧的创建和销毁就已经完成了



相关文章
|
8月前
|
存储 安全 C语言
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-2
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-2
|
8月前
|
存储 编译器 C语言
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-1
深度剖析c语言程序 -- 函数栈帧的创建和销毁(纯肝货)-1
104 0
|
8月前
|
存储 小程序 编译器
c语言内功修炼--深度剖析数据的存储
c语言内功修炼--深度剖析数据的存储
|
5月前
|
存储 C语言
【C语言】——函数栈帧的创建与销毁
【C语言】——函数栈帧的创建与销毁
|
8月前
|
存储 编译器 C语言
C语言内功修炼--指针详讲(进阶)
C语言内功修炼--指针详讲(进阶)
|
8月前
|
存储 C语言
C语言内功修炼---指针详讲(初阶)
C语言内功修炼---指针详讲(初阶)
|
8月前
|
存储 编译器 C语言
C语言:底层剖析——函数栈帧的创建和销毁
C语言:底层剖析——函数栈帧的创建和销毁
|
8月前
|
存储 编译器 程序员
C语言之反汇编查看函数栈帧的创建与销毁(二)
C语言之反汇编查看函数栈帧的创建与销毁(二)
|
1天前
|
算法 C语言
【C语言程序设计——函数】利用函数求解最大公约数和最小公倍数(头歌实践教学平台习题)【合集】
本文档介绍了如何编写两个子函数,分别求任意两个整数的最大公约数和最小公倍数。内容涵盖循环控制与跳转语句的使用、最大公约数的求法(包括辗转相除法和更相减损术),以及基于最大公约数求最小公倍数的方法。通过示例代码和测试说明,帮助读者理解和实现相关算法。最终提供了完整的通关代码及测试结果,确保编程任务的成功完成。
25 15
|
1天前
|
C语言
【C语言程序设计——函数】亲密数判定(头歌实践教学平台习题)【合集】
本文介绍了通过编程实现打印3000以内的全部亲密数的任务。主要内容包括: 1. **任务描述**:实现函数打印3000以内的全部亲密数。 2. **相关知识**: - 循环控制和跳转语句(for、while循环,break、continue语句)的使用。 - 亲密数的概念及历史背景。 - 判断亲密数的方法:计算数A的因子和存于B,再计算B的因子和存于sum,最后比较sum与A是否相等。 3. **编程要求**:根据提示在指定区域内补充代码。 4. **测试说明**:平台对代码进行测试,预期输出如220和284是一组亲密数。 5. **通关代码**:提供了完整的C语言代码实现
36 24