IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 【C语言】函数栈帧的创建与销毁 -> 正文阅读

[C++知识库]【C语言】函数栈帧的创建与销毁


前言

随着不断地学习C语言,相信每个人都可能存在着一些疑惑?

1.局部变量是怎么创建的?
2.为什么局部变量的值是随机值?
3.函数是怎样传参的?传参的顺序是怎样的?
4.形参与实参是什么关系?
5.函数调用是怎样做的?
6.函数调用结束后怎样返回的?


这篇文章所使用的环境是VS2013,在不同的编译器下,函数调用过程中栈帧的创建与销毁是有所不同的,具体细节取决于编译器的实现,当然大体逻辑结构都是类似的。

一、前期准备工作

1.C语言函数代码

#include <stdio.h>

int Add(int a, int b)
{
	int x = 0;
	x = a + b;
	return x;
}

int main()
{
	int a = 10;
	int b = 20;
	int ret = Add(a, b);
	printf("%d\n", ret);
	return 0;
}

2.寄存器了解

EBP:扩展基址指针寄存器(extended base pointer) ,用于存放函数栈底指针。
ESP:扩展栈指针寄存器(Extended Stack Pointer),用于存放函数栈顶指针。

而这两个指针就是维护这次函数调用创建的栈帧。
在这里插入图片描述

二、函数调用分步式讲解

首先大家要明白的是,在进入到main内部时,我们通过调试窗口的函数调用堆栈观察到 main 函数被调用起来了(上图),那么main函数被谁调用了呢,在代码调试完之后可以在反汇编代码626行看到 __tmainCRTStartup()调用了main函数,同样,该函数又被 在466行的mainCRTStartup()所调用(下图)。那么在为main函数开辟栈帧空间前还要为这两个函数的调用开辟栈帧空间,这两个函数不是重点,我们从main函数的调用开始讲解。

在这里插入图片描述

在这里插入图片描述

1.main函数栈帧开辟

先为main函数的第一行代码创建一个断点,按F10进入调试窗口,找到反汇编点击进入反汇编代码;在看这部分代码要知道的是此前已经为__tmainCRTStartup()函数开辟了栈帧空间,ebp指向栈底,esp指向栈顶。

//此时ebp和esp分别指向__tmainCRTStartup()函数的栈帧空间的栈底和栈顶

push        ebp      //压栈,在__tmainCRTStartup()函数的栈帧空间上方开辟一块空间并将ebp的值给到这块空间;
                     //开辟完之后esp指针向上移动指向这块空间
mov         ebp,esp  //将esp的值给到ebp,那么ebp指针就会向上移动,指向当前esp所指的位置,此时两个指针指向同一块地址
sub         esp,0E4h //esp减去这个16进制的数字,那么esp指针向上移动指向差值的这个位置

//此时,这两个指针指向的这块空间就是为main函数所开辟的栈帧空间

push        ebx  //这三个语句就是在main函数栈帧空间上方压栈三块空间,esp继续向上移动这三块空间
push        esi  
push        edi  

lea         edi,[ebp-0E4h]  //load effective address 加载有效地址;               
                            //将ebp地址减去 0E4h 刚好指向main空间的顶部这个位置,然后赋值给edi

mov         ecx,39h  
mov         eax,0CCCCCCCCh  //这三条指令的作用是从edi位置开始,将下方的所有空间全部初始化为0CCCCCCCCh;
rep stos    dword ptr es:[edi]  //总共初始化39次,其实也就是将main函数的栈帧空间全部初始化。

2.创建函数变量

	int a = 10;
0024141E  mov         dword ptr [ebp-8],0Ah  //把16进制数字0Ah放到[ebp-8]这块空间上
	int b = 20;
00241425  mov         dword ptr [ebp-14h],14h  //把16进制数字14h放到[ebp-14h]这块空间上

在这里插入图片描述
如果在创建局部变量时没有对其初始化,那么该变量就会被赋值为最开始初始化的值,也就是一个随机值。

3.Add函数传参

int ret = Add(a, b);
0024142C  mov         eax,dword ptr [ebp-14h]  //将[ebp-14h]的数字(20)放到eax中;
0024142F  push        eax  //压栈(20),同时esp指针向上移动
00241430  mov         ecx,dword ptr [ebp-8]  //将[ebp-8]的数字(10)放到ecx中;
00241433  push        ecx  //压栈(10)
00241434  call        002410E1  //调用函数
00241439  add         esp,8 //在调用完了Add函数之后要通过该地址找到acll指令的下一条的指令继续执行;
							//所以还得将这块地址放在栈顶空间上,esp指针指向这块空间。

在这里插入图片描述
注:从这一步就可以看到,函数传参的顺序是从右向左的,压入的两个参数就为形参,它们各自拥有不同的空间,这就是为什么改变形参的值而不会影响实参,形参只不过是一份临时拷贝罢了。

4.Add函数调用

进入到这个函数内部时,同样得先为Add函数创建一块栈帧空间,而开辟方式与main函数的开辟方式一样。

//观察上图就为现在main函数的栈帧空间
//下面的代码就真正开始了Add函数的的调用
int Add(int a, int b)
{
00DD13C0  push        ebp  //压栈,在栈顶开辟一块空间并将ebp的值存入,esp向上移动
00DD13C1  mov         ebp,esp  //将esp的值给到ebp,那么ebp指针就会向上移动,指向当前esp所指的位置
							   //此时两个指针指向同一块地址
00DD13C3  sub         esp,0CCh  //esp减去这个16进制的数字,那么esp指针向上移动指向差值的这个位置

//此时,这两个指针指向的这块空间就是为Add函数所开辟的栈帧空间

00DD13C9  push        ebx  
00DD13CA  push        esi  
00DD13CB  push        edi  
00DD13CC  lea         edi,[ebp+FFFFFF34h]  
00DD13D2  mov         ecx,33h  
00DD13D7  mov         eax,0CCCCCCCCh  
00DD13DC  rep stos    dword ptr es:[edi]  //Add函数空间初始化

在这里插入图片描述

4.Add函数内容执行

	int x = 0;
00DD13DE  mov         dword ptr [ebp-8],0  //在ebp向上移动八个字节的这块空间就为 x 的空间,并将内容初始化为0
	x = a + b;
00DD13E5  mov         eax,dword ptr [ebp+8]  //[ebp+8]--> ebp指针向下移动八个字节找到10存到eax中
00DD13E8  add         eax,dword ptr [ebp+0Ch]  //[ebp+0Ch]--> ebp指针向下移动十二个字节找到20与eax中的10相加,
											   //再将和放入到eax中
00DD13EB  mov         dword ptr [ebp-8],eax  //将eax中的值存放到[ebp-8],也就是 x 的空间
}

在这里插入图片描述在这里插入图片描述

5.Add函数返回

	return x;
00DD13EE  mov         eax,dword ptr [ebp-8]  //返回值,在函数执行完后将在[ebp-8]处的运算结果传给eax寄存器
											 //通过eax寄存器将结果返回
00DD13F1  pop         edi  
00DD13F2  pop         esi  
00DD13F3  pop         ebx  
00DD13F4  mov         esp,ebp  
00DD13F6  pop         ebp  //这五条语句是将ADD函数的栈帧空间进行释放,并且ebp,esp返回到main函数的栈帧空间
00DD13F7  ret 	//此时Add函数已经执行完,通过这个ret就会找到mian函数空间的 call指令的下一条地址,继续执行下一条代码										 

每次 pop 完成之后相应的esp,ebp指针都会调整其指向的位置,在(pop ebp)完成之后ebp指向之前在mian函数空间的位置,而esp会指向call指令下一条指令的地址。

6.call指令的后续

00DD1439  add         esp,8  //esp+8向下调整指向main函数空间的 edi 处,此时就将之前所创建的两个形参释放掉
00DD143C  mov         dword ptr [ebp-20h],eax  //将eax中的值返回到[ebp-20h]中,这块空间就为ret的空间
	
	printf("%d\n", ret);//后续就为返回值的打印
00DD143F  mov         esi,esp  
00DD1441  mov         eax,dword ptr [ebp-20h]  
00DD1444  push        eax  
00DD1445  push        0DD5858h  
00DD144A  call        dword ptr ds:[00DD9114h]  
00DD1450  add         esp,8  
00DD1453  cmp         esi,esp  
00DD1455  call        00DD113B  
	return 0;
00DD145A  xor         eax,eax  

三、疑惑解答

1.局部变量是怎么创建的?
答:在每一个函数的栈帧空间中都会初始化一段空间,然后再给局部变量在这块已初始化的空间分配空间。

2.为什么局部变量的值是随机值?
答:因为局部变量的所处的空间最开始会被初始化为 0CCCCCCCCh,所以局部变量的值是随机值。

3.函数是怎样传参的?传参的顺序是怎样的?
答:在函数调用之前就会将参数进行压栈,当使用时通过指针偏移量找到形参(形参并不会在调用函数的栈帧空间内,而是在被调用函数的空间栈帧内),传参的顺序是从右向左依次进行的。

4.形参与实参是什么关系?
答:形参是实参在压栈是所开辟的空间,两者有各自独立的空间,相同的数值。

5.函数调用是怎样做的?
答:函数调用通过call指令找到调用函数的地址,并且将call指令的下一条指令的地址进行压栈,在调用函数返回时通过该地址找到下一条要执行的指令。

6.函数调用结束后怎样返回的?
答:首先将返回值通过寄存器传递,esp,ebp指针分别会指向调用函数执行前在被调用函数原来的位置,而esp指针就会指向之前保存的call指令的下一条指令的地址,通过该地址继续执行。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-10-08 20:22:11  更:2022-10-08 20:23:26 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 4:02:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码