大家可能会函数栈帧不了解,可能都没有听过这个,不用着急,在理解函数栈帧之前,我们先来了解一下程序对内存使用的分区大概情况:

 区域 作用
栈区(stack) 由编译器自动分配和释放,存放函数的参数值,局部变量的值等。操作方式类似与数据结构中的栈
堆区(heap) 一般由程序员分配和释放,若程序员不释放,程序结束时可能由操作系统回收。与数据结构中的堆是两码事,分配方式类似于链表
静态区(static) 全局变量和静态变量存放于此

那么函数栈帧又是什么呢?从逻辑上讲栈帧就是一个函数执行的环境。际上,栈帧可以简单理解为:栈帧就是存储在栈上的每一次函数调用涉及的相关信息的记录单元。

栈是从高地址向低地址分配内存的。每调用一个函数的时候,函数都有它自己的函数栈帧去维护,这个栈帧中维护着所需要的各种信息。

再了解函数栈帧的概念之后,我们还需要学习一些寄存器:

寄存器名称 作用

eax

累加(Accumulator)寄存器,常用于函数返回值
ebx 基址(Base)寄存器,以它为基址访问内存
ecx 基址(Base)寄存器,以它为基址访问内存
edx 数据(Data)寄存器,常用于乘除法和I/O指针
esi 源变址寄存器
dsi 目的变址寄存器
esp 堆栈(Stack)指针寄存器,指向堆栈顶部
ebp 基址指针寄存器,指向当前堆栈底部
eip 指令寄存器,指向下一条指令的地址

下面用一个简单的函数调用去介绍函数栈帧:

#define _CRT_SECURE_NO_WARNINGS
#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 c = add(a, b);printf("%d", c);return 0;
}

然后进去vs2019的函数调试中,进入反汇编看函数的具体过程,首先是函数的初始化(我的是从高地址指向低地址,是往上画的):

int main()
{
00B018B0  push        ebp
00B018B1  mov         ebp,esp
00B018B3  sub         esp,0E4h
00B018B9  push        ebx
00B018BA  push        esi
00B018BB  push        edi
00B018BC  lea         edi,[ebp-24h]
00B018BF  mov         ecx,9
00B018C4  mov         eax,0CCCCCCCCh
00B018C9  rep stos    dword ptr es:[edi]
00B018CB  mov         ecx,0B0C003h
00B018D0  call        00B0131B  

 前面的布置都是main函数的栈帧

 int a = 10;
00B018D5  mov         dword ptr [ebp-8],0Ah  int b = 20;
00B018DC  mov         dword ptr [ebp-14h],14h

 int c = add(a, b);
00B018E3  mov         eax,dword ptr [ebp-14h]
00B018E6  push        eax
00B018E7  mov         ecx,dword ptr [ebp-8]
00B018EA  push        ecx
00B018EB  call        00B01023

int add(int x, int y)
{
00B01850  push        ebp
00B01851  mov         ebp,esp
00B01853  sub         esp,0CCh
00B01859  push        ebx
00B0185A  push        esi
00B0185B  push        edi
00B0185C  lea         edi,[ebp-0Ch]
00B0185F  mov         ecx,3
00B01864  mov         eax,0CCCCCCCCh
00B01869  rep stos    dword ptr es:[edi]
00B0186B  mov         ecx,0B0C003h
00B01870  call        00B0131B 

这里可以看和和main函数初始化的时候基本上没有什么差别,这里就不做讲解,留给读者自己去思考,可参考前面main函数初始化的情况去看。

 int z = 0;
00B01875  mov         dword ptr [ebp-8],0  z = x + y;
00B0187C  mov         eax,dword ptr [ebp+8]
00B0187F  add         eax,dword ptr [ebp+0Ch]
00B01882  mov         dword ptr [ebp-8],eax  return z;
00B01885  mov         eax,dword ptr [ebp-8]
}

00B01888  pop         edi
00B01889  pop         esi
00B0188A  pop         ebx
00B0188B  add         esp,0CCh
00B01891  cmp         ebp,esp
00B01893  call        00B01244
00B01898  mov         esp,ebp
00B0189A  pop         ebp
00B0189B  ret  

esp+0CCh为什么就指向了ebp的位置,大家可以直接动手去调试,看看内存那一块,就知道为什么了 ,然后cmp指令到pop ebp指令都是在销毁add函数的栈帧

ret指令让我们回到了第一次调用add函数的地方(因为在调用的时候,我们已经记住了这个地址,所以可以返回到main函数的栈帧里面去):

00B018F0  add         esp,8
00B018F3  mov         dword ptr [ebp-20h],eax  printf("%d", c);
00B018F6  mov         eax,dword ptr [ebp-20h]
00B018F9  push        eax
00B018FA  push        0B07B30h
00B018FF  call        00B010D2
00B01904  add         esp,8  return 0;

然后就是在ebp-20h的位置创建了局部变量c,并把eax的值赋给了它,后面在进行打印,最后把main函数的栈帧销毁(和add函数的栈帧的销毁类似)

总结一下:1.局部变量的创建是在为当前包含局部变量的函数初始化一片空间之后,在这个空间内给局部变量进行初始化;2.为什么局部变量不初始化的时候是随机值,我们可以看见前面在对函数栈帧进行初始化的时候,里面的被分配的空间值一部分被赋值成了0CCCCCCCCh这个值,不同的编译器初始化的值也可能不同,而局部变量的创建是在那些被初始化为随机值的地方去创建的,所以说局部变量不进行初始化的时候就是随机值;3.函数传参的顺序,我们也可以看到是从右往左传参的,并且形参是实参的临时拷贝,因为在调用函数之前我们已经把实参进行了拷贝并且压入栈中了;

由于本人对函数栈帧的了解还不够深入,只能给各位讲到现在这样,以后还会发布关于函数栈帧的文章,到时候会更加详细的给大家进行讲解,望大家理解,希望这篇文章能给大家带来一点帮助,上述的内容有什么不对的地方,欢迎大家在评论区留言指正。

C语言内功修炼之函数栈帧的创建与销毁(举例加图解)相关推荐

  1. 内功修炼《函数栈帧的创建和销毁》建议收藏

    文章目录 前言 一. 寄存器的概念 二. 通用寄存器的结构 三. 指针寄存器和变址寄存器 四. EBP和ESP 五.总结 前言 在前期的学习过程中,我们可能会有很多的困惑: 1️⃣ 局部变量是怎么创建 ...

  2. 【C语言】程序员筑基功法——《函数栈帧的创建与销毁》

    <函数栈帧的创建与销毁> 文章目录 1. 前言 2. 问题引入 3. 前提准备 3.1 寄存器 3.2 汇编指令 4. 函数栈帧的维护 5. 如何调用堆栈 6. 函数栈帧的创建和销毁 6. ...

  3. 函数调用过程详解:函数栈帧的创建与销毁

    前言:我们在学习C语言的过程中,可以会产生很多疑问,比如: 局部变量是怎么创建的 为什么局部变量的值不做初始化就是随机值 函数是怎么传参的?传参的顺序是怎么样的? 形参和实参是什么关系? 函数调用是怎 ...

  4. (动图详解)汇编视角观察函数栈帧的创建和销毁

    目录 ​1.阅读本文的价值 ​2.函数栈帧及栈的概念 ​3.部分寄存器及汇编指令 ​4.main函数的调用 5.main函数的栈帧创建 ​6.变量的栈帧创建 ​6.函数传参 ​7.函数内部运算及销毁 ...

  5. 函数栈帧的创建与销毁

    目录 前言 一.预备知识 1.内存区域的划分和分配 2.栈帧简介 3.寄存器简介 二.函数栈帧介绍 1.源代码 2.如何查看汇编代码 3.函数栈帧的创建与销毁(重点) 三.小彩蛋 总结 前言 最近在学 ...

  6. 程序员内功心法之函数栈帧的创建和销毁

    目录 1.本节目标 2.相关寄存器 3.相关汇编指令 4.什么是函数栈帧 5.什么是调用堆栈 6.函数栈帧的创建和销毁 (1).main函数栈帧的创建与初始化 (2).main函数的核心代码 (3). ...

  7. 程序员内功修炼——函数栈帧的创建与销毁

    一.什么是函数的栈帧 c语言是由函数构成的,那么函数是如何进行传参的?如何调用的?如何返回值的?这些问题与函数的栈帧有关. 函数栈帧:就是函数调用过程中程序的调用栈所开辟的空间,这些空间用来存放: 1 ...

  8. C语言的函数调用过程(栈帧的创建与销毁)

    从汇编的角度解析函数调用过程 看看下面这个简单函数的调用过程: 1 int Add(int x,int y)2 {3 int sum = 0;4 sum = x + y;5 return sum;6 ...

  9. 函数栈帧的创建和销毁图解

    目录 一.问题: 二.寄存器 栈区 1.寄存器有哪些?有什么作用? 2.编译环境 3.栈区的使用习惯: 4.main函数也是被其他函数调用的 5.汇编代码 三.为main函数创建栈帧 1.main函数 ...

最新文章

  1. jSearch(聚搜) v1.1.0 发布,全新视觉体验
  2. java 查看文件属性_java File类获取文件属性详解
  3. c++中c_str()函数
  4. Error:NgdBuild:604解决方法(添加NGC文件方法)
  5. 【CV论文阅读】Rank Pooling for Action Recognition
  6. runC爆严重安全漏洞,主机可被攻击!使用容器的快打补丁
  7. 最新突破!科学家研发出世界首款精神控制手臂机器人;近日Kubernetes 1.15 正式发布;雷军公布小米手机产品线布局……...
  8. arduino智能风扇系统
  9. 关于properties.load()出现的空指针异常
  10. c++ windows forms 使用_从头开始了解和使用Hypervisor(第1部分)
  11. C++冒泡排序(包含初级、正宗及改进三种实现)
  12. raw转bmp程序c语言,求指导,如何用c语言实现读取*.raw格式图像
  13. 解读德国政府未来可能采用的加密即时通讯框架
  14. 成都梵羽汇:抖音任务中心如何操作呢?
  15. 2015 UESTC 数据结构专题A题 秋实大哥与小朋友 线段树 区间更新,单点查询,离散化...
  16. 《高质量C/C++编程指南》摘要
  17. C Primer Plus(6) 中文版 第3章 数据和C 3.4 C语言基本数据类型
  18. 三十八. geotrellis使用 COG 写入和读取
  19. wpf 导航菜单_WPF:精简导航菜单
  20. 2017年12月英语六级翻译洞庭湖

热门文章

  1. 2017GDKOI酱油记
  2. C语言算法:冒泡法排序
  3. ubuntu 添加中文拼音输入法
  4. CSS精灵图代码示例
  5. 数据分析编程检验——车流量统计(不能使用pandas和numpy)
  6. iOS 扫描二维码/条形码
  7. linux内核 checksum,Linux内核分析 - 网络[十三]:校验和
  8. js正则验证身份证号码(最后一位x不区分大小写)
  9. 安装 Windows Server 2003 IIS6.0 详解(图)
  10. MY SQL 数据库库如何下载以及安装