函数栈帧的创建与销毁(最详细的一集)上

前言

1.我们在进行c语言代码编程的时候,常常会把独立的一个功能抽象为函数,利用函数去实现各种的功能,那么,函数是如何调用的?函数的返回值是怎么返回的?参数又是如何传参的?所有这些问题都会跟函数栈帧问题有关。

正片开始:

1.什么是函数栈帧

具体来说,函数栈帧就是在函数调用过程中在程序的调用栈开辟的空间,而这些空间就用来存放:

1.函数参数和函数的返回值

2.临时变量(包括函数的非静态的局部变量一集编译器自动产生的其他临时变量)

3.保存上下文信息(包括函数调用前后需要保持不变的寄存器)

2.理解了函数栈帧后能解决什么问题?

我认为理解这些是一种修炼”内功“的行为,在理解了函数栈帧的创建与销毁后,以下的问题便迎刃而解了

1.局部变量是怎么在栈区中创建的

2.为什么局部变量不出货内容是随机的?

3.函数调用时参数是怎么传递的?传递的顺序是怎么样的?

4函数的实参和形参是怎么分别实例化的

函数栈帧的创建和销毁解析

栈(stack)是现代计算机中最重要的概念之一,每一个程序都用到了栈,没有栈就没有函数,也就没有各种计算机语言,

栈被定义为一种特殊的容器,我们也会在数据结构中学到栈,用户可以将数据压入栈(push),也可以将入栈的数据弹出(pop),但是必须遵守的一个规则是:先入后出,先入栈的数据后出栈(就跟弹匣一样,压入子弹,射出子弹)

在计算机系统中,栈是一个具有以上属性的动态内存区域,程序可以将数据压栈,也可以将数据出栈,压栈会让栈增长,出栈会让栈减小。

在经典的操作系统,栈总是向下增长的(高地址向低地址)

在我们常见的i386或者X86-64下,栈顶是由esp的寄存器进行定位(涉及汇编指令)。

相关寄存器和汇编指令

相关寄存器

eax:通用寄存器,保留临时数据,常用于返回值

ebx:通用寄存器,保留临时数据

ebp:栈底寄存器

esp:栈顶寄存器

eip:指令寄存器,保存当前指令的下一条指令的地址

rbp:是 x86-64 架构中的寄存器,它在汇编语言中通常用于构建函数调用栈帧(stack frame)。

rdi:是 x86-64 架构中的通用寄存器之一。在函数调用中,rdi 通常用于传递函数的第一个参数。在 x86-64 架构中,函数的前六个参数(整数或指针类型)通常通过 rdirsirdxrcxr8r9 这六个通用寄存器传递

相关汇编命令

mov:数据转移指令

push:数据入栈,同时esp栈顶寄存器也要发生改变

pop:数据弹出至指定位置,同时esp栈顶寄存器也要发生改变

sub:减法命令

add:加法命令

call:函数调用,1. 压入返回地址 2. 转入目标函数

jump:通过修改eip,转入目标函数,进行调用

ret:恢复返回地址,压入eip,类似pop eip命令

lea:用于加载有效地址(Load Effective Address)。虽然 lea 的名称中包含 "load" 一词,但它实际上并不执行内存加载操作。相反,lea 计算一个有效地址,并将结果存储到目标操作数中

解析函数栈帧的创建与销毁

1.预备知识

1.在每一次的函数调用中,都需要为本次函数调用开辟空间,也就是函数栈帧的空间。

2.这块空间的维护是用的两个寄存器,esp和ebp,ebp记录栈底地址,esp记录栈顶地址。

如图

在一个函数的调用过程中,会出现以下的过程,这里举一个简单的例子说明

#include <stdio.h>
int Add(int x, int y)
{
int z = 0;
z = x + y;
return z;
}
int main()
{
int a = 3;
int b = 5;
int ret = 0;
ret = Add(a, b);
printf("%d\n", ret);
return 0;
}

以上我举了一个简单的加法函数,我们可以用VS(观测函数栈帧使用越低的版本越容易观察

我们打开VS调试的反汇编,查看相对应的汇编代码

 
int main()
{
00007FF7C47518D0  push        rbp  (首先把rbp寄存器压栈)
00007FF7C47518D2  push        rdi  push rdi 表示将 rdi 的当前值保存到栈上。这是为了在函数执行期间保护 rdi 的值,以便在后续的代码中使用)
00007FF7C47518D3  sub         rsp,148h   (减去rsp的空间,表示将栈指针 rsp 的值减去 148h(也可以写作 0x148328),即在栈上分配 328 字节的空间)
00007FF7C47518DA  lea         rbp,[rsp+20h]  (将rsp加上20h的空间,也就是分配“32”字节的偏移量
00007FF7C47518DF  lea         rcx,[__A7141C24_test@c (07FF7C4761008h)]  (加载一个地址到“rcx”寄存器)
00007FF7C47518E6  call        __CheckForDebuggerJustMyCode (07FF7C4751370h)  
    int a = 1;(调用函数,这里因为编译器优化的原因,在 这里就i已经知道 a是下面Add函数的参数,所以Add 函数的调用移到初始化 a 的位置
00007FF7C47518EB  mov         dword ptr [a],1  (变量声明初始化a,将常量1存储到a)
    int b = 2;
00007FF7C47518F2  mov         dword ptr [b],2  (变量声明初始化b,将常量2存储到b)
    int ret = Add(a, b);
00007FF7C47518F9  mov         edx,dword ptr [b]  (将变量B存进edx寄存器)
00007FF7C47518FC  mov         ecx,dword ptr [a]  (将变量A存进ecx寄存器)
00007FF7C47518FF  call        Add (07FF7C4751348h)  (调用Add函数)
00007FF7C4751904  mov         dword ptr [ret],eax  
    printf("%d", ret);
00007FF7C4751907  mov         edx,dword ptr [ret]  
00007FF7C475190A  lea         rcx,[string "%d" (07FF7C4759C24h)]  
00007FF7C4751911  call        printf (07FF7C4751195h)(call调用printf函数)  
    return 0;
00007FF7C4751916  xor         eax,eax  
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【NLP练习】Pytorch文本分类入门

Pytorch文本分类入门 &#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客 &#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制一、前期准备 1. 环境安装 确保已经安装torchtext与portalocker库 2. 加载数据 #加载数据 import torch import t…

【滑动窗口】Leetcode 找到字符串中所有字母异位词

题目解析 438. 找到字符串中所有字母异位词 算法讲解 寻找目标串的异位词&#xff0c;我们使用固定长度的滑动窗口&#xff0c;首先我们判断窗口左右的字符是否存在于目标串中&#xff0c;如果不存在就让窗口滑动&#xff1b;存在的话&#xff0c;我们就把字符丢进Hash中&a…

【JavaSE】类和对象详解(上)

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 类和对象 类的组成 对类的理解 成员变量的访问和类方法的调用 this 抛出一个问题 this的作用 初始化成员变量 未初始化的成员变量 代码举例 就地初始化 构…

每日一题(leetcode331):验证二叉树的前序序列化——栈

类似消消乐&#xff0c;数字&#xff0b;“#”“#”就可以消成一个“#”&#xff0c;到最后如果栈中只剩一个“#”便说明序列正确。 当然也可以用槽位理解&#xff0c;一个数字出现会消耗一个槽位产生两个槽位&#xff08;即产生一个槽位&#xff09;&#xff0c;一个“#”出现…

题目:安全序列(蓝桥OJ 3432)

问题描述&#xff1a; 题解&#xff1a; #include <bits/stdc.h> using namespace std; using ll long long; const int N 1e6 9, p 1e9 7;int prefix[N],dp[N];int main() {int n, k;cin >> n >> k;dp[0] prefix[0] 1;for(int i 1; i < n; i){i…

iptables 与 firewalld 防火墙

iptables iptables 是一款基于命令行的防火墙策略管理工具 四种防火墙策略&#xff1a; ACCEPT&#xff08;允许流量通过&#xff09; 流量发送方会看到响应超时的提醒&#xff0c;但是流量发送方无法判断流量是被拒绝&#xff0c;还是接收方主机当前不在线 REJECT&#xff08…

STM32的IAP技术,BootLoader

来源 三种下载方式&#xff1a; 1、ICP&#xff1a;ST-Link, 2、ISP: FlyMcu, 3、IAP IAP简介 IAP技术的核心在于BootLoader程序的设计&#xff0c;这段程序预先烧录在单片机中&#xff0c;正常的APP程序可以使用BootLoader程序中的IAP功能写入&#xff0c;也可以两部分代码一…

【JavaEE初阶系列】——常见的锁策略

目录 &#x1f6a9;乐观锁和悲观锁 &#x1f6a9;读写锁和普通互斥锁 &#x1f6a9;轻量级锁和重量级锁 &#x1f6a9;自旋锁和挂起等待锁 &#x1f6a9;公平锁和非公平锁 &#x1f6a9;可重入锁和不可重入锁 &#x1f6a9;关于synchronized的锁策略以及自适应 接下来讲解的锁策…

Springboot之RESTful风格

目录 1、概述&#xff1a; 1.1、传统风格的API&#xff1a; 1.2、RESTful风格的API&#xff1a; 1.3、GET、POST、PUT、DELETE&#xff1a; 2、RESTful风格相关的注解&#xff1a; ①PathVariable&#xff0c;用来获取url中的数据&#xff1b; ②GetMapping&#xff0c;接…

C++教学——从入门到精通 6.ASCII码与字符型

如何把小写字母转换成大写字母呢&#xff1f; 这个问题问的好&#xff0c;首先我们要新学一个类型——char 这个类型就是字符型 再来说说ASCII码 给大家举几个例子 空格————32 0————48 9————57 A————65 Z————90 a————97 z————122 我们…

Cortex-M7中断向量表的重定向

1 前言 系统上电后&#xff0c;PC会指向复位向量&#xff0c;即向量表中的Reset_Handler&#xff0c;而系统就是通过Vector Table Offset Register (VTOR)的值加上4字节来找到复位向量的入口的。 因为地址 0 处应该存储引导代码&#xff0c;所以它通常映射到 Flash 或者是 ROM …

Unity类银河恶魔城学习记录11-14 p116 Thunder strike item effect源代码

Alex教程每一P的教程原代码加上我自己的理解初步理解写的注释&#xff0c;可供学习Alex教程的人参考 此代码仅为较上一P有所改变的代码 【Unity教程】从0编程制作类银河恶魔城游戏_哔哩哔哩_bilibili ThurderStrike_Controller.cs using System.Collections; using System.C…

如何提高小红书笔记的收录率?

在小红书平台上&#xff0c;笔记的收录率是衡量一篇笔记是否受欢迎和有价值的重要因素。为了提高笔记的收录率&#xff0c;有几个关键点需要注意&#xff1a; 1.内容不涉及广告 在发布笔记前要先确保笔记内容不包含任何形式的广告或推广信息。小红书平台对于广告性质的内容有…

关于Ansible的模块②

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 接《关于Ansible的模块 ①-CSDN博客》&#xff0c;继续学习和梳理Ansible的常用文件类模块 1. copy模块 从当前机器上复制文件到…

关于 Unreal 的各种坐标系、输入与逻辑的转换问题

说明 已知&#xff1a; 在世界原点往 X 轴方向看去&#xff0c;ForwardVector 为 [ 1 , 0 , 0 ] [1,0,0] [1,0,0]&#xff0c;此时的右手边的方向为 [ 0 , 1 , 0 ] [0,1,0] [0,1,0] 手柄摇杆、鼠标移动朝右得到的 [ 1 , 0 , 0 ] [1,0,0] [1,0,0]&#xff0c;朝上得到的是…

代码随想录:字符串5-7

右旋字符串 题目 字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k&#xff0c;请编写一个函数&#xff0c;将字符串中的后面 k 个字符移到字符串的前面&#xff0c;实现字符串的右旋转操作。 例如&#xff0c;对于输入字符…

货币与利率

货币与利率 货币及其职能什么是货币货币的职能货币带来了什么&#xff1f; 货币形式的演变商品货币代用货币信用货币货币的特性 现代社会货币的表现形式流通中的现金支票存款信用卡储存存款 货币层次划分目的划分标准划分种类我国的货币层次 货币与物价的关系利率什么是利息什么…

算法学习——LeetCode力扣补充篇1

算法学习——LeetCode力扣补充篇1 1365. 有多少小于当前数字的数字 1365. 有多少小于当前数字的数字 - 力扣&#xff08;LeetCode&#xff09; 描述 给你一个数组 nums&#xff0c;对于其中每个元素 nums[i]&#xff0c;请你统计数组中比它小的所有数字的数目。 换而言之&a…

推荐一本牛逼的入门 Python书!,如何试出一个Python开发者真正的水平

本书详细解说了 Python 语言和编程的本质&#xff0c;无论你是否接触过编程语言&#xff0c;只要是 Python 编程的初学者&#xff0c;都可阅读本书。 本书讲解的内容虽然基础&#xff0c;但并不简单。本书提供了 165 幅图表&#xff0c;可以让大家能够轻松地理解并掌握复杂的概…

Taro+vue3 监听当前的页面滚动的距离

1.需求 想实现一个这样的效果 一开始这个城市组件 是透明的 在顶部 的固定定位 当屏幕滑动的时候到一定的距离 将这个固定的盒子 背景颜色变成白色 2.Taro中的滚动 Taro中的滚动 有固定的api 像生命周期一样 这个生命周期是 usePageScroll import Taro, { useDidShow, useP…