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语言初阶】函数栈帧的创建与销毁

本篇文章,博主所使用的环境是VS2013,建议在学习函数栈帧的创建与销毁不要使用太高级的编译器,越高级的编译器,越不容易观察函数栈帧创建与销毁的过程。同时函数栈帧的创建和销毁的过程在不同的编译器下,它的创建和销毁是略有差异的,大体逻辑是一致的,具体细节取决于编译器的实现。

在了解函数栈帧的创建与销毁之前我们先了解一下什么是函数栈帧。

每一个函数调用,都要在栈区上创建一个空间,而在栈区上为函数创建的空间就叫做函数栈帧

接下来我们就开始学习函数栈帧的创建和销毁吧。

铺垫:
1.寄存器:
eax
ebx
ecx
edx
ebp
esp

重点是esp和ebp这两个寄存器,这两个寄存器中存放的是地址,是用来维护函数栈帧的。
栈帧的维护如下:
在这里插入图片描述

下面会进行详细讲解:

接下来我们用一个简单的代码来进行讲解:

int Add(int x,int y)
{
	int z = 0;
	z = x + y;
	return z;
}
int main()
{
	int a = 10;
	int b = 20;
	int c = 0;
	
	c = Add(a,b);
	printf("%d\n",c);
	return 0;
}

调试程序查看堆栈:
在这里插入图片描述
从上图可以看出,在vs2013中,main函数是被__tmainCRTStartup函数调用的,而__tmainCRTStartup函数是被mainCRTStartup函数调用的,所以两者也都要在栈区开辟空间

接着转到反汇编:
在这里插入图片描述

接下来我们对函数栈帧的创建和销毁一步一步进行说明:
1、调用main()函数之前:
我们上方说到main函数是被__tmainCRTStartup函数调用的,所以首先为__tmainCRTStartup函数开辟空间并且进行维护,如图:
在这里插入图片描述

2.main函数栈帧的创建:
main函数栈帧的创建过程:
第一步:
调用反汇编的第一条指令:

009214F0  push        ebp  

push是压栈的意思:将ebp进行压栈处理(把ebp放入栈顶),压栈后,esp自动指向栈顶:
在这里插入图片描述
第二步
调用第二条指令:

009214F1  mov         ebp,esp  

mov是赋值的意思:将esp的值给ebp,此时产生新的ebp,即ebp指向esp指向的位置:
在这里插入图片描述
第三步:
第三条指令:

009214F3  sub         esp,0E4h  

sub为减的意思,将esp-0E4h的值赋给esp,且函数调用分配由高地址向低地址增长,因此esp向上移动,即开辟了新空间:
在这里插入图片描述
第四步

009214F9  push        ebx  
009214FA  push        esi  
009214FB  push        edi 

三个push压栈,分别将ebx,esi,edi按顺序压入栈顶,而esp也会自动指向栈顶:
在这里插入图片描述
第五步:

009214FC  lea         edi,[ebp+FFFFFF1Ch]  //[ebp+FFFFFF1Ch]==[ebp-0E4h] 
00921502  mov         ecx,39h  
00921507  mov         eax,0CCCCCCCCh  
0092150C  rep stos    dword ptr es:[edi] 

lea的意思是加载有效地址:将ebp-0E4h的有效地址加载到edi中;
第二句的意思是:把39h放到ecx中去;
第三句的意思是:把0CCCCCCCCh 的值放到ecx中去
最后一句的意思是:从edi的位置开始,把eax里的内容按4个字节拷贝ecx次,放到edi向下的位置(即把main函数栈帧里的内容全部初始化为0CCCCCCCCh ):
在这里插入图片描述
第六步:
接下来看下面三句指令:
在这里插入图片描述
把10放到epb-8的位置(图中的0Ah就是十六进制的10);
把20放到epb-14h的位置(14h是十六进制的20);
把0放到epb-20h的位置。

我们来看看它们在内存中是如何存储的:
在这里插入图片描述
所以它们在栈区上存储的位置如图:
在这里插入图片描述
第七步:
我们对abc三个变量初始化完成之后,接下来走到Add函数,调用Add函数又要为其开辟栈帧,还有进行传参操作,接下来我们来看看它们在内存中究竟是如何进行操作的吧!

在这里插入图片描述

00EF1523  mov         eax,dword ptr [ebp-14h]  
00EF1526  push        eax  
00EF1527  mov         ecx,dword ptr [ebp-8]  
00EF152A  push        ecx 
00EF152B  call        00EF1226  
00EF1530  add         esp,8  
00EF1533  mov         dword ptr [ebp-20h],eax  

1.把ebp-14h所在地址的值,也就是b的值,放入eax这个寄存器中,然后对eax进行压栈;

2、然后把ebp-8所在地址的值,也就是a的值,放入ecx这个寄存器中,然后接着对ecx进行压栈;
其实这两步在进行传参操作

3.call的作用是调用函数区:将下一条指令的地址压栈,然后进入add函数里面。
这里为什么要将call指令的下一条指令的地址进行压栈呢?
因为当Add函数调用完之后,需要返回来,而返回来之后需要继续执行call指令的下一条指令,所以需将call指令的下一条指令的地址进行压栈。

在这里插入图片描述
此时main函数的栈帧就会变得如图所示,esp也自动指向栈顶

接着按F11进入Add函数中:
看Add函数的汇编代码:
在这里插入图片描述
我们可以看出,Add函数的汇编代码上半部分,和main函数中的前半部分极为相似,其实这与main函数一样,为Add函数开辟函数栈帧
如图:
在这里插入图片描述
然后接着往下走:
在这里插入图片描述
把ebp-8所指向的空间初始化为0,即创建变量z
然后ebp+8所指向的空间里的内容(a的值)赋给eax
接着把ebp+och所指向的空间里的内容(b的值)加到eax中去
然后再把eax里的值给ebp-8里去,即赋给z

如图:
在这里插入图片描述
其实在这里x就是ecx里的10,y就是eax里的20。
这也说明了,形参是实参的一份临时拷贝,而且传参的时候,先传b,再传a。

这样就完成了相加的过程。

接下来就是要将相加的值返回去,也就是返回z的值。
我们来看看是如何返回的:
在这里插入图片描述

首先把ebp-8里的值(z的值)放到eax寄存器里面去。因为函数调用完后,而为函数所开辟的函数栈帧会被销毁,但寄存器不会随着程序的退出而销毁,所以这样就可以将值返回去。

然后接着看下面的指令:

三个pop: pop是出栈操作,将edi esi ebx依次从上向下出栈,esp自动下移动。
然后将ebp值赋给esp,也就是esp向下移动到指向main函数的ebp的位置,此时add开辟的栈空间已经销毁
如图:
在这里插入图片描述
然后接着pop ebp:弹出ebp,也就是说此时的ebp返回到main函数的栈底,此时我们就返回到main函数的栈帧
当执行ret后,程序就会返回到我们上文所说的call指令的下一条指令,这也就是为什么上文会将call指令的下一条指令的地址进行压栈。执行完之后,这个地址也将会出栈:
在这里插入图片描述
返回到main函数之后,继续执行call指令的下一条指令:
在这里插入图片描述

把esp+8,即esp向下移,把形参销毁
然后把eax里的值(30)放到ebp-20h?中
在这里插入图片描述
最后就是打印C的值,然后main结束之后销毁main函数的栈帧:
在这里插入图片描述
总结:
学习完函数栈帧的创建与销毁后,我们就可以清楚的知道,局部变量是如何创建的,为什么局部变量的值是随机的,以及函数是如何传参的,传参顺序如何,还有函数调用后是如何返回的。这些问题都迎刃而解了。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-08-09 10:03:37  更:2021-08-09 10:04:01 
 
开发: 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/9 21:46:35-

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