函数栈帧的创建和销毁(详细理解)

2024-05-15 23:44

本文主要是介绍函数栈帧的创建和销毁(详细理解),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

🎁个人主页:我们的五年

🔍系列专栏:c语言课程学习

🎉欢迎大家点赞👍评论📝收藏⭐文章

目录

 问题:

1.ebp,esp两个寄存器用来维护函数栈帧

2.main函数也一个函数,main函数也要被其他函数调用

 3.函数栈帧创建的过程

4.函数栈帧的销毁过程


 

 问题:

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

调用函数的时候,会为函数开辟一块空间,然后第一个局部变量从栈低分配一块空间给局部变量。

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

因为在为函数开辟空间的时候,这块空间的里的内存都被初始化为一个值,不同编译器中值可能不同。如果只是创建局部变量,没有初始化覆盖里面的值,那此时局部变量里的值就是为函数内存初始化的值。

3.函数是怎么传参的?传参的顺序是怎样的?

调用函数的时候,会在该函数的栈顶压栈,该内存里放的是形参,然后通过ebp的偏移量就可以找到形参。        传参的顺序是从右到左。

4.形参和实参是什么关系?

形参是实参的零时拷贝,形参是在函数栈顶新开辟的一块空间,这块空间与实参的空间相互独立,只是数值一样。改变形参不会改变实参。

5.函数调用是怎么做的?

函数会把函数的下一指令的地址保存在call中,并且保存ebp的位置。

然后为新的函数开辟空间,在新的函数的栈顶压入三个寄存器ebx,esi,edi,然后新的空间全部初始化为一个值。然后就执行新新函数里的指令。

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

返回值是通过寄存器带回来的,寄存器是全局的,不会因为函数的销毁而销毁。

然后根据保存的ebp,和下一指令的地址找到新的函数,即要执行的指令的地址。

不同的函数会开辟不同的空间。

1.ebp,esp两个寄存器用来维护函数栈帧

1.ebp寄存器:栈底寄存器。

2.esp寄存器:栈顶寄存器。

3.pc指针寄存器:也叫程序计数器,它永远指向当前指令的下一条指令。

当前有下面代码:

#define CRT_SECURE_NO_WARNINGS 1
#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 = 0;c = Add(a, b);printf("%d", c);return 0;
}

 

 ●这个程序中,我们先进入main函数,esp寄存器和ebp寄存器就用来维护main函数的函数栈帧。当指令到达c=Add(a,b);的时候,就要调用Add函数,这时候,esp寄存器和ebp寄存器就要区维护Add函数的函数栈帧。

●每一个函数都有自己的ebp和esp,即每个函数都有自己的栈顶和栈底。

●在CPU中,ebp和esp不会有很多,但是函数却有很多。但是程序运行的时候,不可能一边运行两个函数,当运行main函数的时候,ebp和esp去维护main函数的函数栈帧。当进入Add函数,去运行Add函数的时候,esp和ebp就去维护Add函数的函数栈帧,并且把mian函数的esp和ebp的指针保存下来,等Add函数结束以后,esp和ebp就又可以去维护main函数的函数栈帧。

●地址的使用顺序是从高地址,到低地址。

2.main函数也一个函数,main函数也要被其他函数调用

当main函数结束以后,函数栈帧回到__tmianCRTStartup()。

●由此可知main函数被__tmainCRTStartup()调用。

●__tmainCRTStartup()函数被mainCRTStartup()函数调用。

 

 

 程序先进入mainCRTStartup函数,为mainCRTStartup函数开辟函数栈帧,然后调用__tmainCRTStartup函数,为__tmainCRTStartup函数开辟函数栈帧。所以上面的图中比main高地址处还有这两个函数的函数栈帧,这两个函数的函数栈帧比main函数的函数栈帧高。

 3.函数栈帧创建的过程

在调用main函数时,已经存在来到下面情形,__tmainCRTStartup()调用main函数

🚗1.push  ebp:在栈顶,在栈顶把ebp指针的值放进去。esp就要改变位置,往上移一点,栈顶改变,esp也要改变。指针的大小是四个字节,在栈顶存ebp的值,所以就开辟4个字节的空间,里面放着ebp的值。

🚗 2.mov        ebp,esp:把esp的值给ebp,那么ebp就指向esp指向的地方。

🚗3.sub        esp,0E4H:0E4H是一个16进制数,转化为10进制是228.让esp减小228。减小是往上增长,往低地址处变化。

🚗4.push        ebx

        push        esi

        push        edi:

        在栈顶压入ebx,esi,edi,esp跟着变化。

🚗5.lea=lode effective address

把edi往下的到ebp内存里的值全部变为0cccccccch.word是两个字节,dword是double word占4个字节。

🚗6显示地址以后:

🚗7.Add函数调用

 [ebp+8]找到a,[ebp+0ch]找到b,b给eax,然后eax+(add)a,然后eax给z。

Add函数中没有创建形参a,b。

4.函数栈帧的销毁过程

【ebp-8】表示z,也就是把z的值给寄存器eax,这样z的值就不会丢失。

 三次pop销毁edi,esi,ebx。

ebp的值给esp,esp就指向ebp指向的位置。

然后pop        ebp,把ebp指向空间的值给ebp也就是main  ebp。

然后ret  call的下一条指令的地址。使程序可以从main函数调用完Add函数以后,执行Add函数的下一条指令。

然后把main函数的指令执行完。

最后把main函数的函数栈帧给销毁。

回到__tmainCRTStartup函数。

这篇关于函数栈帧的创建和销毁(详细理解)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/993262

相关文章

Spring Boot集成Druid实现数据源管理与监控的详细步骤

《SpringBoot集成Druid实现数据源管理与监控的详细步骤》本文介绍如何在SpringBoot项目中集成Druid数据库连接池,包括环境搭建、Maven依赖配置、SpringBoot配置文件... 目录1. 引言1.1 环境准备1.2 Druid介绍2. 配置Druid连接池3. 查看Druid监控

创建Java keystore文件的完整指南及详细步骤

《创建Javakeystore文件的完整指南及详细步骤》本文详解Java中keystore的创建与配置,涵盖私钥管理、自签名与CA证书生成、SSL/TLS应用,强调安全存储及验证机制,确保通信加密和... 目录1. 秘密键(私钥)的理解与管理私钥的定义与重要性私钥的管理策略私钥的生成与存储2. 证书的创建与

使用Docker构建Python Flask程序的详细教程

《使用Docker构建PythonFlask程序的详细教程》在当今的软件开发领域,容器化技术正变得越来越流行,而Docker无疑是其中的佼佼者,本文我们就来聊聊如何使用Docker构建一个简单的Py... 目录引言一、准备工作二、创建 Flask 应用程序三、创建 dockerfile四、构建 Docker

MySQL 中的 CAST 函数详解及常见用法

《MySQL中的CAST函数详解及常见用法》CAST函数是MySQL中用于数据类型转换的重要函数,它允许你将一个值从一种数据类型转换为另一种数据类型,本文给大家介绍MySQL中的CAST... 目录mysql 中的 CAST 函数详解一、基本语法二、支持的数据类型三、常见用法示例1. 字符串转数字2. 数字

Python设置Cookie永不超时的详细指南

《Python设置Cookie永不超时的详细指南》Cookie是一种存储在用户浏览器中的小型数据片段,用于记录用户的登录状态、偏好设置等信息,下面小编就来和大家详细讲讲Python如何设置Cookie... 目录一、Cookie的作用与重要性二、Cookie过期的原因三、实现Cookie永不超时的方法(一)

Python内置函数之classmethod函数使用详解

《Python内置函数之classmethod函数使用详解》:本文主要介绍Python内置函数之classmethod函数使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录1. 类方法定义与基本语法2. 类方法 vs 实例方法 vs 静态方法3. 核心特性与用法(1编程客

Python函数作用域示例详解

《Python函数作用域示例详解》本文介绍了Python中的LEGB作用域规则,详细解析了变量查找的四个层级,通过具体代码示例,展示了各层级的变量访问规则和特性,对python函数作用域相关知识感兴趣... 目录一、LEGB 规则二、作用域实例2.1 局部作用域(Local)2.2 闭包作用域(Enclos

从原理到实战深入理解Java 断言assert

《从原理到实战深入理解Java断言assert》本文深入解析Java断言机制,涵盖语法、工作原理、启用方式及与异常的区别,推荐用于开发阶段的条件检查与状态验证,并强调生产环境应使用参数验证工具类替代... 目录深入理解 Java 断言(assert):从原理到实战引言:为什么需要断言?一、断言基础1.1 语

SpringBoot整合liteflow的详细过程

《SpringBoot整合liteflow的详细过程》:本文主要介绍SpringBoot整合liteflow的详细过程,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋...  liteflow 是什么? 能做什么?总之一句话:能帮你规范写代码逻辑 ,编排并解耦业务逻辑,代码

MySQL count()聚合函数详解

《MySQLcount()聚合函数详解》MySQL中的COUNT()函数,它是SQL中最常用的聚合函数之一,用于计算表中符合特定条件的行数,本文给大家介绍MySQLcount()聚合函数,感兴趣的朋... 目录核心功能语法形式重要特性与行为如何选择使用哪种形式?总结深入剖析一下 mysql 中的 COUNT