修炼内功之函数栈帧的创建与销毁

ops/2024/12/25 20:39:51/

修炼内功之函数栈帧的创建与销毁

  • 一 前置知识
    • (1)栈
    • (2)相关寄存器和汇编指令
  • 二 函数栈帧
  • 三 代码演示函数栈帧的创建
    • (1)代码演示
    • (2)函数栈帧逐帧分析
  • 四 对开篇问题的解答

  相信来CSDN的伙伴们,或多或少对编程都有一定的了解,那本菜鸟来考考你,关于编程的绝命六连问,看看你能答对几个?

1.局部变量是如何创建的?·
2.为什么局部变量不初始化内容是随机的?
3.有些时候屏幕上输出的"烫烫烫"是怎么来的?
4.函数调用时参数时如何传递的?传参的顺序是怎样的?。
5.函数的形参和实参分别是怎样实例化的?
6.函数的返回值是如何返回的?

  或许这些问题一出,有些小伙伴们可能就懵了,我们都知道函数,函数调用,我们也知道局部变量,形参,实参等等,这些我们每次写代码都在用的东西,看似简单,深入思考之后,发现别有洞天,这些告诉我们,学习编程,不可以浮于表面。
  要解决这些问题,我们就得深入理解函数栈帧的创建与销毁。

一 前置知识

(1)栈

  一般我们在学习 C/C ++语言的时候.我们会关注内存中的三个区域:栈区、堆区、静态区。

1.局部变量,函数参数是放在内存的栈区
2.全局变量,静态变量是放在内存的静态区
3.堆区是用来动态内存管理的

在这里插入图片描述

由此,我们知道函数是在栈中开辟空间的,那什么是栈呢?

  栈:在经典的计算机科学中,栈被定义为一种特殊的容器,用户可以将数据压入栈中(入栈, push) ,也可以将已经压入栈中的数据弹出(出栈, pop ),但是栈这个容器必须遵守一条规则:先入栈的数据后出栈 (First ln Last 0ut, FIFO) 。就像叠成一叠的书,先上去的书在最下面,因此要最后才能取出。
  在经典的操作系统中,栈总是向下增长(由高地址向低地址)的。栈顶由成为 esp 的寄存器进行定位的。

(2)相关寄存器和汇编指令

1.相关寄存器

eax :通用寄存器,保留临时数据,常用于返回值ebx :通用寄存器,保留临时数据
ebp :栈底寄存器
esp :栈顶寄存器
eip :指令寄存器,保存当前指令的下一条指令的地址

2.相关汇编指令

mov :数据转移指令
push :数据入栈,同时 esp 栈顶寄存器也要发生改变
pop :数据弹出至指定位置,同时 esp 栈顶寄存器也要发生改变
sub :减法命令
add :加法命令
cal l :函数调用, I. 压入返回地址 2.转入目标函数jump :通过修改 eip,转入目标函数,进行调用
ret :恢复返回地址,压入 eip ,类似 pop eip 命令

二 函数栈帧

  概念:每一次函数的调用,操作系统都会在内存的栈区上开辟一块空间,称为栈帧。函数调用建立栈帧,栈帧中存储局部变量,参数等。
  学习函数的栈帧,就必须要了解ebp,esp(ebp 存放的是栈底的地址,esp 存放的是栈顶的地址,),专门用于维护函数栈帧的。
在这里插入图片描述

三 代码演示函数栈帧的创建

(1)代码演示

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

  首先,我先简单的声明一下,main函数也是被其他函数调用起来的,在vs2019中, main 函数调用之前,是由invoke-main 函数来调用 main 函数。在 invoke-main 函数之前的函数调用我们就暂时不考虑了。那我们可以确定, invoke-main 函数应该会有自已的栈帧, main 函数和 Add 函数也会维护自已的栈帧,每个函数栈帧都有自已的 ebp 和 esp 来维护栈帧空间。
  那接下来我们从 main 函数的栈帧创建开始讲解:

(2)函数栈帧逐帧分析

1.转到反汇编
在这里插入图片描述
2.逐帧分析如图
在这里插入图片描述

四 对开篇问题的解答

1.局部变量是如何创建的?
在这里插入图片描述
  局部变量的创建是在局部变量所在的函数的栈帧创建完成并初始化后,然后在该栈帧内为局部变量分配空间的。
2.为什么局部变量不初始化其内容是随机的?
  因为编译器在创建函数栈帧后会在栈帧空间里面放入一个值,而这个值是随机的。
3.有些时候屏幕上输出的"烫烫烫"是怎么来的?

#include<stdio.h>
int main()
{
char arr[20];
printf("%s\n",arr);
return 0;
}

  调试输出“烫烫烫……”的原因,是因为main函数调用时,在栈区开辟的空间的其中每一个字节都被初始化为0xCC,而arr数组是一个未初始化的数组,恰好在这块空间上创建的,0xCCCC(两个连续排列的0xCC)的汉字编码就是“烫”,所以0xCCCC被当作文本就是“烫”。
4 ,函数调用时参数时如何传递的?传参的顺序是怎样的?
在这里插入图片描述

  我们在调用函数之前,就会在栈顶上从右向左依次压入需要传递的参数,在创建好被调函数的函数栈帧后通过指针的偏移量来使用传递过去的参数,而不是在被调函数的函数栈帧内创建形参。
5 .函数的形参和实参的关系是什么?
  形参是实参的一份临时拷贝,二者的存储亻立置不同,形参的改变不会影响实参。
6 .函数的返回值是如何带回的?
在这里插入图片描述

  函数的返回值通过 eax寄存器带回。
(完)


http://www.ppmy.cn/ops/144933.html

相关文章

deepin 安装 zookeeper

deepin 安装 zookeeper 1、升级软件 sudo apt updatesudo apt -y dist-upgrade2、安装常用软件 sudo apt -y install gcc make openssl libssl-dev libpcre3 libpcre3-dev libgd-dev \rsync openssh-server vim man zip unzip net-tools tcpdump lrzsz tar wget3、开启ssh …

mysql联表查询

创建多个表&#xff0c;语句如下&#xff1a; CREATE DATABASE /*!32312 IF NOT EXISTS*/sg_security /*!40100 DEFAULT CHARACTER SET utf8mb4 */;USE sg_security;/*Table structure for table sys_menu */DROP TABLE IF EXISTS sys_menu;CREATE TABLE sys_menu (id bigint(2…

《信管通低代码信息管理系统开发平台》Linux环境安装说明

1 简介 信管通低代码信息管理系统应用平台提供多环境软件产品开发服务&#xff0c;包括单机、局域网和互联网。我们专注于适用国产硬件和操作系统应用软件开发应用。为事业单位和企业提供行业软件定制开发&#xff0c;满足其独特需求。无论是简单的应用还是复杂的系统&#xff…

单元测试mock框架Mockito

为了继续改进 Mockito 并进一步改善单元测试体验&#xff0c;我们希望您升级到 2.1.0&#xff01;Mockito 遵循语义版本控制&#xff0c;仅在主要版本升级时包含重大更改。在库的生命周期中&#xff0c;重大更改是推出一组全新功能所必需的&#xff0c;这些功能会改变现有行为甚…

重温设计模式--状态模式

文章目录 状态模式&#xff08;State Pattern&#xff09;概述状态模式UML图作用&#xff1a;状态模式的结构环境&#xff08;Context&#xff09;类&#xff1a;抽象状态&#xff08;State&#xff09;类&#xff1a;具体状态&#xff08;Concrete State&#xff09;类&#x…

VSCode 性能优化指南:提高编码效率,减少资源占用

Visual Studio Code&#xff08;简称VSCode&#xff09;是一款广受欢迎的代码编辑器&#xff0c;以其强大的功能和丰富的插件生态系统著称。然而&#xff0c;随着项目规模的扩大和插件数量的增加&#xff0c;VSCode 的性能可能会受到影响。本文将介绍一系列优化措施&#xff0c…

QT多媒体开发(一):概述

Qt Multimedia 模块为多媒体编程提供支持。多媒体编程实现的功能主要包括播放音频和视频文件&#xff0c;通过麦克风录制音频&#xff0c;通过摄像头拍照和录像等。 QT6 中多媒体模块相比QT5变化较大&#xff0c;所以用QT6编译 QT5写的多媒体 程序基本无法通过。 Qt 5 多媒体模…

Spring AOP 中记录日志

Spring AOP 中记录日志 使用 AOP 和 Spring 提供的 RequestContextHolder 在通知中记录 HTTP 请求相关日志。以下是进阶添加日志功能的完整例子和说明。 完整示例 1. 切面类实现 Aspect Component public class LogAspect {Around("annotation(log)") // 拦截所有…