【C语言】——函数栈帧的创建与销毁

2024-05-25 01:44

本文主要是介绍【C语言】——函数栈帧的创建与销毁,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

函数栈帧的创建与销毁

本文主要讲解了函数调用过程中其栈帧的创建与销毁,内容干货较多,希望大家认真品味。

使用C语言进行函数调用时,是否会有很多疑问:
1.局部变量是如何创建的?
2.局部变量在未初始化的情况下,其值为什么会是随机值?
3.函数是如何进行传参的?
4.函数在传参的过程中顺序是怎样的?
5.形参和实参是什么关系?
6.函数调用是如何实现的?
7.函数在调用结束后如何返回的?

这些问题应该都困扰大家许久,本文应该会给大家提供些许思路!

在进行讲解之前,大家应该都会了解C语言的一个关键字register——寄存器: 【关键字】——register在C语言中的使用,寄存器中有eax,ebx,ecx,edx,ebp,esp等。

在函数栈帧中会存在ebp,esp俩个寄存器,这俩个寄存器中存放的是地址,这俩个地址是用来维护函数栈帧的。

每一次的函数调用都会在栈区开创一个空间(包括main函数)。

#include<stdio.h>
int add(int x, int y)
{int z = 0;z = x + y;return z;
}
int main(void)
{int a = 10;int b = 20;int ret = 0;ret = add(a, b);return 0;
}

在这里我们使用vs2022来观察函数栈帧的创建与销毁。

同时,本次调试的环境是X86环境

在这里插入图片描述

  • 将代码转为反汇编

在这里插入图片描述

  • 打开监视、调用堆栈、内存方便观察

在这里插入图片描述

  • 转为反汇编

在这里插入图片描述

在函数调用开始前,我们应该了解到main函数也是被其他函数调用的,这点在vs2022中没有体现

在这里插入图片描述

main函数是被__mainCRTStartup函数调用的,__mainCRTStartup函数是被mainCRTStartup函数调用的。

esp与ebp俩个寄存器是用来维护函数栈帧的俩个寄存器。

在这里插入图片描述
首先执行第一条汇编代码:push ebp

push——压栈,即从栈顶存放一个数据
pop——出栈,即从栈顶拿出一个数据

在这里插入图片描述

此时,将epb的地址存放在__mainCRTStartup函数顶部,移动寄存器esp

在这里插入图片描述

执行下一条汇编代码:move ebp,esp

move——即将后面的值赋给前面

在这里插入图片描述
此时,将esp的地址赋予ebp,二者指向同一地址

在这里插入图片描述
下一个汇编代码:sub sep,0E4h

sub——即submit,将前面的数据减去后面数据
0E4h——E4个字节,即228个字节

在这里插入图片描述
此时,将寄存器esp向上移动228个字节

在这里插入图片描述

下面三条汇编代码:
push ebx
push esi
push edi
即将ebx,esi,edi三个寄存器压栈

在这里插入图片描述

此时,将ebx,esi,edi三个寄存器的地址放入顶部

在这里插入图片描述

下面一条汇编代码:lea edi,[ebp-24h]
lea——load effective address即加载有效地址
将[ebp-24h]的地址加载到edi寄存器上

在这里插入图片描述

此时,处于栈区的edi记录了ebp-24h的地址
在这里插入图片描述
ebp-24h的地址为:0x0095FAEC

在这里插入图片描述

下面一条汇编代码:mov ecx,9
move——将后面的值赋给前面,即将9保存在寄存器ecx中

在这里插入图片描述
该寄存器的地址未保存在栈,这里阐述一下,将寄存器压栈,即保存寄存器的地址,然后改变寄存器内部数据是不影响寄存器在栈地址的保存的,如果了解指针的话,对这里的认知应该会更加清晰的。

在这里插入图片描述
下一条汇编代码:move eax,0CCCCCCCCh
将0CCCCCCCCh移动正在eax寄存器中

在这里插入图片描述
此时,将0CCCCCCCCh移动到寄存器中

在这里插入图片描述
下一条汇编代码:rep stos dword ptr es:[edi]
rep——repeat重复操作
stos——store string 存储字符串
rep stos——重复存储字符串操作
dword——double word双字,即四个字节
ptr——pointer指针
即用指针找到edi上存储的[ebp-24h],将[ebp-24h]下面的9个dword,即9个四个字节的数字初始化为0CCCCCCCCh.

在这里插入图片描述
由于画图面积有限,画出初始化部分0CCCCCCCCh.

在这里插入图片描述
可以在内存窗口看见初始化部分。

在这里插入图片描述
下一条汇编代码:call 00CA1320
call——跳转到子程序的地址
此时,表明已经创建好一个函数,即main函数已经创建完成,通过call一个地址,进入此函数中

在这里插入图片描述
下一条汇编代码:mov dword ptr [ebp-8],0Ah
0Ah——转为十进制为10
通过一个指针找到[ebp-8]的地址,将0Ah的值放入该地址

在这里插入图片描述

此时,在栈区存储局部变量0Ah

在这里插入图片描述
通过地址可以查看到[ebp-8]这个地址的存储值

在这里插入图片描述
下一条汇编代码:mov dword ptr [ebp-14h],14h
14h——转为十进制为20
通过一个指针找到[ebp-14h]的地址,将14h的值放入该地址

在这里插入图片描述
此时,在栈区存储局部变量014h

在这里插入图片描述
通过地址可以查看到[ebp-14h]这个地址的存储值

在这里插入图片描述
下一条汇编代码:mov dword ptr [ebp-20h],0
通过一个指针找到[ebp-20h]的地址,将0的值放入该地址

在这里插入图片描述
此时,在栈区存储局部变量0

在这里插入图片描述

通过地址可以查看到[ebp-20h]这个地址的存储值

在这里插入图片描述

下一条汇编代码:mov eax,dword ptr [ebp-14h]
将[ebp-14h]这个地址保存的【值】保存在eax寄存器中

在这里插入图片描述

此时,寄存器eax中保存[ebp-14h]中的值

在这里插入图片描述
下一条汇编代码:push eax
将寄存器eax的地址压栈

在这里插入图片描述
此次行动可以称为传参。

在这里插入图片描述
可以通过监视器观察到,寄存器中存的是20这个值,而非地址

在这里插入图片描述

下面俩条代码是同样的操作:
mov ecx,dword ptr[ebp-8]
push ecx
将[ebp-8]这个地址里的值保存在ecx寄存器中,利用寄存器将值压栈

在这里插入图片描述

传参过程,即将[ebp-8]的保存的值传到寄存器ecx中

在这里插入图片描述

可以通过监视器观察到,寄存器中存的是10这个值,而非地址

在这里插入图片描述
下一条汇编代码:call 00921023
即跳转到子程序中,此时点击F11,进入add函数内部

在这里插入图片描述

接下来的操作,是创建函数过程,与创建main函数过程相同,本人使用图例演示。

下一条汇编代码:push ebp
在这里插入图片描述

下一条汇编代码:mov ebp,esp

在这里插入图片描述
下一条汇编代码:sub esp,0CCh

在这里插入图片描述

下一条汇编代码: push ebx

在这里插入图片描述

下一条汇编代码: push esi

在这里插入图片描述

下一条汇编代码: push edi

在这里插入图片描述

下一条汇编代码: lea edi,[ebp-0Ch]

在这里插入图片描述

下一条汇编代码: mov ecx,3

在这里插入图片描述

下一条汇编代码: mov eax,0CCCCCCCCh

在这里插入图片描述

下一条汇编代码:rep stos dword ptr es:[edi]

在这里插入图片描述

下一条汇编代码:mov ecx,92C008h

在这里插入图片描述

下一条汇编代码: call 00921320

在这里插入图片描述

在这里插入图片描述

下一条汇编代码:mov dword ptr [ebp-8],0

在这里插入图片描述

在这里插入图片描述

下一条汇编代码:mov eax,dword ptr [ebp+8]

在这里插入图片描述

将[ebp+8]中存储的20放入寄存器中

在这里插入图片描述

下一条汇编代码:add eax,dword ptr [ebp+0Ch]

在这里插入图片描述

将俩个地址的值放在寄存器中相加

在这里插入图片描述

下一条汇编代码:dword ptr [ebp-8],eax

在这里插入图片描述
将寄存器eax中的数值放在[ebp-8]

在这里插入图片描述

下一条汇编代码:eax,dword ptr [ebp-8]

在这里插入图片描述

将z中的数值保存在寄存器中

在这里插入图片描述

下一条汇编代码:pop edi
pop——出栈,即从顶上拿出一个元素

在这里插入图片描述
edi被释放

在这里插入图片描述
下一条汇编代码:pop esi

在这里插入图片描述
esi被释放

在这里插入图片描述

下一条汇编代码:pop ebx

在这里插入图片描述
ebx被释放

在这里插入图片描述

下一条汇编代码:esp,0CCh

在这里插入图片描述

将维护函数的esp向栈底移动
在这里插入图片描述
下一条汇编代码:cmp ebp,esp

cmp——compare比较,功能相当与减法指令,但不保存结果

在这里插入图片描述
下一条汇编代码:call 00461244
即跳转到下一个程序中。

在这里插入图片描述

下一条汇编代码:mov esp,ebp

在这里插入图片描述
释放add函数,将esp与ebp寄存器返回

在这里插入图片描述
下一条汇编代码:pop ebp

在这里插入图片描述

释放存放ebp的地址

在这里插入图片描述

下一条汇编代码:ret

ret——return,返回

在这里插入图片描述
下一条汇编代码:add esp,8

在这里插入图片描述

将esp向下移动8个字节,释放存放局部变量的地方

在这里插入图片描述
下一条汇编代码:mov dword ptr [ebp-20h],eax

在这里插入图片描述
将寄存器eax中存放的30放在[ebp-20h]地址中

在这里插入图片描述

下一条汇编代码:xor eax,eax
xor——XOR指令进行按位逻辑异或操作,将结果存放在目标操作数中1。它可以将一个寄存器的值和一个常量进行异或操作,结果会存储到原寄存器中2。
大家还记得异或操作符吗?——相同为0,相异为1

在这里插入图片描述

那么俩个相同的值进行异或会?哈哈当然会变成0啊!

在这里插入图片描述

下面这些操作是释放main函数,main函数也是被调用的

在这里插入图片描述

  • 今天的内容就到这里啦!希望大家可以提出建议!!!让我多多学习!!

这篇关于【C语言】——函数栈帧的创建与销毁的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1000128

相关文章

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

Python itertools中accumulate函数用法及使用运用详细讲解

《Pythonitertools中accumulate函数用法及使用运用详细讲解》:本文主要介绍Python的itertools库中的accumulate函数,该函数可以计算累积和或通过指定函数... 目录1.1前言:1.2定义:1.3衍生用法:1.3Leetcode的实际运用:总结 1.1前言:本文将详

Go语言中三种容器类型的数据结构详解

《Go语言中三种容器类型的数据结构详解》在Go语言中,有三种主要的容器类型用于存储和操作集合数据:本文主要介绍三者的使用与区别,感兴趣的小伙伴可以跟随小编一起学习一下... 目录基本概念1. 数组(Array)2. 切片(Slice)3. 映射(Map)对比总结注意事项基本概念在 Go 语言中,有三种主要

Python创建Excel的4种方式小结

《Python创建Excel的4种方式小结》这篇文章主要为大家详细介绍了Python中创建Excel的4种常见方式,文中的示例代码简洁易懂,具有一定的参考价值,感兴趣的小伙伴可以学习一下... 目录库的安装代码1——pandas代码2——openpyxl代码3——xlsxwriterwww.cppcns.c

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

MySQL数据库函数之JSON_EXTRACT示例代码

《MySQL数据库函数之JSON_EXTRACT示例代码》:本文主要介绍MySQL数据库函数之JSON_EXTRACT的相关资料,JSON_EXTRACT()函数用于从JSON文档中提取值,支持对... 目录前言基本语法路径表达式示例示例 1: 提取简单值示例 2: 提取嵌套值示例 3: 提取数组中的值注意

C语言中自动与强制转换全解析

《C语言中自动与强制转换全解析》在编写C程序时,类型转换是确保数据正确性和一致性的关键环节,无论是隐式转换还是显式转换,都各有特点和应用场景,本文将详细探讨C语言中的类型转换机制,帮助您更好地理解并在... 目录类型转换的重要性自动类型转换(隐式转换)强制类型转换(显式转换)常见错误与注意事项总结与建议类型

使用Python在Excel中创建和取消数据分组

《使用Python在Excel中创建和取消数据分组》Excel中的分组是一种通过添加层级结构将相邻行或列组织在一起的功能,当分组完成后,用户可以通过折叠或展开数据组来简化数据视图,这篇博客将介绍如何使... 目录引言使用工具python在Excel中创建行和列分组Python在Excel中创建嵌套分组Pyt

Go语言利用泛型封装常见的Map操作

《Go语言利用泛型封装常见的Map操作》Go语言在1.18版本中引入了泛型,这是Go语言发展的一个重要里程碑,它极大地增强了语言的表达能力和灵活性,本文将通过泛型实现封装常见的Map操作,感... 目录什么是泛型泛型解决了什么问题Go泛型基于泛型的常见Map操作代码合集总结什么是泛型泛型是一种编程范式,允