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

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

相关文章

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

hdu1171(母函数或多重背包)

题意:把物品分成两份,使得价值最接近 可以用背包,或者是母函数来解,母函数(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v)(1 + x^v+x^2v+.....+x^num*v) 其中指数为价值,每一项的数目为(该物品数+1)个 代码如下: #include<iostream>#include<algorithm>

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

在cscode中通过maven创建java项目

在cscode中创建java项目 可以通过博客完成maven的导入 建立maven项目 使用快捷键 Ctrl + Shift + P 建立一个 Maven 项目 1 Ctrl + Shift + P 打开输入框2 输入 "> java create"3 选择 maven4 选择 No Archetype5 输入 域名6 输入项目名称7 建立一个文件目录存放项目,文件名一般为项目名8 确定

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

顺序表之创建,判满,插入,输出

文章目录 🍊自我介绍🍊创建一个空的顺序表,为结构体在堆区分配空间🍊插入数据🍊输出数据🍊判断顺序表是否满了,满了返回值1,否则返回0🍊main函数 你的点赞评论就是对博主最大的鼓励 当然喜欢的小伙伴可以:点赞+关注+评论+收藏(一键四连)哦~ 🍊自我介绍   Hello,大家好,我是小珑也要变强(也是小珑),我是易编程·终身成长社群的一名“创始团队·嘉宾”

C++操作符重载实例(独立函数)

C++操作符重载实例,我们把坐标值CVector的加法进行重载,计算c3=c1+c2时,也就是计算x3=x1+x2,y3=y1+y2,今天我们以独立函数的方式重载操作符+(加号),以下是C++代码: c1802.cpp源代码: D:\YcjWork\CppTour>vim c1802.cpp #include <iostream>using namespace std;/*** 以独立函数

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念