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语言的过程中,我们经常会进行函数调用,我们对于函数调用表面现象倒是一清二楚,但是对于其具体在内存中是如何让运作,却不怎么了解,今天我们就来好好探究里面的奥妙。

想要了解函数栈帧的创建和销毁,首先必须了解以下知识点:

知识介绍:

我们电脑CPU中会有一个小型存储区域,用来暂时存放参与运算的数据和运算的结果,那就是寄存器。寄存器具有高速度,低空间的特点。寄存器有eax、ebx、ecx、edx、esi、edi、eip、esp、ebp,我们这里着重介绍esp、ebp。

esp(栈顶指针)、ebp(栈底指针)这两个寄存器中存放的是地址,而这两个地址是用来维护函数栈帧的,具体意思就是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 sum = 0;
	sum = add(a, b);
	return 0;
}

我们在windows10的visual studio 2013编译器(debug、32位,以下简称vs2013)环境下进行测试。为展示效果,代码(如上图)就写的尽可能详细。

在vs2013中通过对main函数定义的查看可以了解,main函数是被一个名叫mainCRTStartup的函数调用,而mainCRTStartup又被一个名叫__tmainCRTStartup的函数调用,因此main函数也会在栈区上开辟属于自己的空间。

用一幅图来总结上面的知识。

?演示函数栈帧的创建和销毁:

我们对代码进行反汇编可以看见函数栈帧创建和销毁的具体过程(如下图所示)。接下来我就把整个过程详细的讲解一番。(反汇编中的地址和栈区中的地址不一样)

1.为main()函数开辟栈帧

?

push压栈,esp的值减少4个字节,然后将ebp的值压入栈中。

move移动,将esp的值赋给ebp,这里并不是将esp所指向的空间赋给ebp,只是单纯的地址赋值。

sub减去,将esp的值减去0E4h字节保存在esp中,即esp向低地址移动0E4h字节。?

将esp的值减少4字节,然后将ebx的值压入栈中。

将esp的值减少4字节,然后将esi的值压入栈中。

将esp的值减少4字节,然后将edi的值压入栈中。

lea将一个内存地址的值直接赋给操作数,rep能够引发其后字符串指令被重复,stos将eax的值中的值拷贝到es:[edi]指向的空间,dword双字(四个字节),ptr指针,es:[edi]指向目的串。

在栈上从edi-0E4h开始的位置,向高地址方向内存赋值0CCCCCCCCh,重复39次,每次赋值双字(4个字节)的空间。

2.main()函数创建局部变量

将0Ah赋给a所在的内存地址指向的空间

将14h赋给b所在内存地址所指向的空间

将0赋给sum所在内存地址所指向的空间

?

3.形参的传递

?

esp的值减少4个字节,将14h的值压入栈中(b的形参,即y)

esp的值减少4个字节,将0Ah的值压入栈中(a的形参,即x)

可以看出,虽然我们写的是int add(int x, int y),但是传参顺序确实从右往左,即先创建y,再创建x

,也知道形参和实参是两块不同空间储存,改变形参不会改变实参,即形参是实参的临时拷贝。

call将下一条指令的地址压入栈中(当子程序执行完之后很根据这个地址返回),并移动到调用的子程序中(跳转到后面的地址)

?

jump将无条件跳转到后面的地址。

?4.为add()函数开辟栈帧

?这里和mian()函数的函数栈帧开辟如出一辙。总的概括就是:

esp减少4字节,将ebp的值压入栈中

将esp的值赋给ebp,这里并不是将esp所指向空间的值赋给ebp

将esp的值减去0CCh字节保存在esp中,即esp向低地址移动0CCh字节。

将esp的值减少4字节,然后将ebx的值压入栈中。

将esp的值减少4字节,然后将esi的值压入栈中。

将esp的值减少4字节,然后将edi的值压入栈中。

在栈上从edi-0CCh开始的位置,向高地址方向内存赋值0CCCCCCCCh,重复33次,每次赋值双字(4个字节)的空间。

?

将0赋值给z所指向的双字节空间

将x所指向的双字节空间的值赋给eax

add是加法运算,将y所指向的双字节空间的值加给eax

将eax的的值赋给z所指向的双字节空间

将z所指向双字节空间的值赋给eax

?5.为add()函数销毁栈帧

取出edi在栈中的值,esp的值增加4字节

取出esi在栈中的值,esp的值增加4字节

取出ebx在栈中的值,esp的值增加4字节

将ebp的值赋给esp,这里并不是将ebp所指向的内存空间的值赋给esp

取出ebp在栈中的值,esp的值增加4字节

ret会取出栈中存放的call下一条指令的地址,用于返回main()函数,esp的值增加4字节

?

给esp的值加上8个字节

将eax的值赋给sum所指向的双字节空间

?6.为main()函数销毁栈帧

可以看出,之后就是main()函数的栈帧销毁了,这里就不详细展开讲了,留给有兴趣的老铁研究研究。

总结:

以上就是函数栈帧创建和销毁的大概流程了,看到这里,想必以前学习中的许多困惑已经有了答案了吧。

比如:

1.局部变量是怎么创建的?

2.为什么局部变量的值是随机值?

3.函数是怎么传参的?

4.传参的顺序是怎么样的?

5.形参和实参什么关系?

6.函数调用以后怎么返回的?

……

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

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