C语言内功修炼之函数栈帧的创建与销毁(举例加图解)
大家可能会函数栈帧不了解,可能都没有听过这个,不用着急,在理解函数栈帧之前,我们先来了解一下程序对内存使用的分区大概情况:
区域 | 作用 |
栈区(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语言内功修炼之函数栈帧的创建与销毁(举例加图解)相关推荐
- 内功修炼《函数栈帧的创建和销毁》建议收藏
文章目录 前言 一. 寄存器的概念 二. 通用寄存器的结构 三. 指针寄存器和变址寄存器 四. EBP和ESP 五.总结 前言 在前期的学习过程中,我们可能会有很多的困惑: 1️⃣ 局部变量是怎么创建 ...
- 【C语言】程序员筑基功法——《函数栈帧的创建与销毁》
<函数栈帧的创建与销毁> 文章目录 1. 前言 2. 问题引入 3. 前提准备 3.1 寄存器 3.2 汇编指令 4. 函数栈帧的维护 5. 如何调用堆栈 6. 函数栈帧的创建和销毁 6. ...
- 函数调用过程详解:函数栈帧的创建与销毁
前言:我们在学习C语言的过程中,可以会产生很多疑问,比如: 局部变量是怎么创建的 为什么局部变量的值不做初始化就是随机值 函数是怎么传参的?传参的顺序是怎么样的? 形参和实参是什么关系? 函数调用是怎 ...
- (动图详解)汇编视角观察函数栈帧的创建和销毁
目录 1.阅读本文的价值 2.函数栈帧及栈的概念 3.部分寄存器及汇编指令 4.main函数的调用 5.main函数的栈帧创建 6.变量的栈帧创建 6.函数传参 7.函数内部运算及销毁 ...
- 函数栈帧的创建与销毁
目录 前言 一.预备知识 1.内存区域的划分和分配 2.栈帧简介 3.寄存器简介 二.函数栈帧介绍 1.源代码 2.如何查看汇编代码 3.函数栈帧的创建与销毁(重点) 三.小彩蛋 总结 前言 最近在学 ...
- 程序员内功心法之函数栈帧的创建和销毁
目录 1.本节目标 2.相关寄存器 3.相关汇编指令 4.什么是函数栈帧 5.什么是调用堆栈 6.函数栈帧的创建和销毁 (1).main函数栈帧的创建与初始化 (2).main函数的核心代码 (3). ...
- 程序员内功修炼——函数栈帧的创建与销毁
一.什么是函数的栈帧 c语言是由函数构成的,那么函数是如何进行传参的?如何调用的?如何返回值的?这些问题与函数的栈帧有关. 函数栈帧:就是函数调用过程中程序的调用栈所开辟的空间,这些空间用来存放: 1 ...
- C语言的函数调用过程(栈帧的创建与销毁)
从汇编的角度解析函数调用过程 看看下面这个简单函数的调用过程: 1 int Add(int x,int y)2 {3 int sum = 0;4 sum = x + y;5 return sum;6 ...
- 函数栈帧的创建和销毁图解
目录 一.问题: 二.寄存器 栈区 1.寄存器有哪些?有什么作用? 2.编译环境 3.栈区的使用习惯: 4.main函数也是被其他函数调用的 5.汇编代码 三.为main函数创建栈帧 1.main函数 ...
最新文章
- jSearch(聚搜) v1.1.0 发布,全新视觉体验
- java 查看文件属性_java File类获取文件属性详解
- c++中c_str()函数
- Error:NgdBuild:604解决方法(添加NGC文件方法)
- 【CV论文阅读】Rank Pooling for Action Recognition
- runC爆严重安全漏洞,主机可被攻击!使用容器的快打补丁
- 最新突破!科学家研发出世界首款精神控制手臂机器人;近日Kubernetes 1.15 正式发布;雷军公布小米手机产品线布局……...
- arduino智能风扇系统
- 关于properties.load()出现的空指针异常
- c++ windows forms 使用_从头开始了解和使用Hypervisor(第1部分)
- C++冒泡排序(包含初级、正宗及改进三种实现)
- raw转bmp程序c语言,求指导,如何用c语言实现读取*.raw格式图像
- 解读德国政府未来可能采用的加密即时通讯框架
- 成都梵羽汇:抖音任务中心如何操作呢?
- 2015 UESTC 数据结构专题A题 秋实大哥与小朋友 线段树 区间更新,单点查询,离散化...
- 《高质量C/C++编程指南》摘要
- C Primer Plus(6) 中文版 第3章 数据和C 3.4 C语言基本数据类型
- 三十八. geotrellis使用 COG 写入和读取
- wpf 导航菜单_WPF:精简导航菜单
- 2017年12月英语六级翻译洞庭湖