当前位置: 首页 > article >正文

【函数栈帧的创建和销毁:一文看懂 C/C++ 函数调用的底层秘密】

本文适合被“局部变量为什么是随机值”、“函数怎么传参”、“返回值怎么带回来”这些问题困扰过的初学者。文末会解释为什么返回局部变量的引用有时能打印出正确值但依然是错的Hello,大家好呀这里是小J,函数栈帧这部分内容可以说是语言学习的基石能深刻的理解函数栈帧很多C/C里面的现象你都不会被绕晕那就让我们一起学习共同进步吧 在我们前期学习时你可能有很多困惑局部变量是怎么创建的为什么局部变量的值是随机值甚至出现“烫烫烫”函数是怎么传参的传参的顺序是怎样的形参和实参是什么关系函数调用是怎么做的函数调用结束后怎么返回的为什么不能返回局部变量的指针/引用但有时候居然能“正确”打印如果你对这些问题有一丝好奇恭喜你 ——你已经准备好揭开计算机最底层的浪漫函数栈帧。 一、两个指针的舞台ebp 和 esp在 x86 架构下CPU 用两个寄存器来管理函数调用时的内存ebp栈底指针 / 帧指针像一个锚点固定当前函数的栈帧底部。esp栈顶指针始终指向当前栈的顶部低地址方向。栈的特点从高地址向低地址生长压栈时esp减小出栈时esp增大。你可以把栈想象成一叠盘子ebp标记最下面那个esp总是盯着最上面的空位。每个函数在执行时都会在栈上“圈”一块属于自己的区域 ——这就是栈帧里面存放局部变量、参数、返回地址等。 二、main 函数也是被“人”调用的你可能认为程序一启动就是main但main也是被启动例程调用的。操作系统加载程序 → 执行启动例程清理环境、获取参数 →call main→ main 返回后再调用exit。所以main的栈帧创建和销毁和普通函数一模一样。 三、从零开始一个函数调用到底发生了什么我们用一个最简单的加法函数来模拟intadd(intx,inty){intzxy;returnz;}intmain(){inta10;intb20;intcadd(a,b);return0;}第 1 步压入参数从右向左 在 call add 之前main 负责把参数压栈顺序是从右向左push b ; 先压 b (20) push a ; 再压 a (10) call add❓ 为什么从右向左为了支持可变参数函数如 printf这样栈顶总能拿到第一个参数的地址。第 2 步call 指令做了两件事call add 执行时把下一条指令的地址压入栈作为返回地址—— 相当于埋了一张“回家的路标”。跳转到 add 函数的代码处。第 3 步被调用函数建立自己的栈帧进入 add 后编译器自动生成序言代码push ebp ; 保存老的栈底指针属于 main mov ebp, esp ; 让 ebp 指向当前栈顶即新栈帧的底部 sub esp, 4 ; 给局部变量 z 腾出空间esp 下移此时栈的结构高地址 ------------------------- | main 的局部变量 a,b | ------------------------- | 参数 b (20) | ← ebp 12 ------------------------- | 参数 a (10) | ← ebp 8 ------------------------- | 返回地址 (main内) | ← ebp 4 ------------------------- | 旧的 ebp 值 (main的) | ← ebp (当前帧底部) ------------------------- | 局部变量 z (未初始化) | ← ebp - 4 (esp 指向这里) ------------------------- 低地址通过 ebp 可以很方便地访问参数ebp8、ebp12和局部变量ebp-4、ebp-8……。第 4 步函数体执行intzxy;对应汇编mov eax, [ebp8] ; 取 a (10) → eax add eax, [ebp12] ; 加上 b (20) → eax 30 mov [ebp-4], eax ; 存到局部变量 z第 5 步返回值与清理mov eax, [ebp-4] ; 把 z 的值 (30) 放入 eax mov esp, ebp ; 收回局部变量空间 (esp 指向 ebp) pop ebp ; 恢复老的 ebp (main 的栈底) ret ; 弹出返回地址并跳转回去关键点返回值通过 eax 寄存器传递不依赖原栈内存。第 6 步调用者清理参数在常见 cdecl 调用约定下调用者负责弹出参数如 add esp, 8恢复栈平衡。然后 main 从 eax 读取返回值给变量 c。 四、那些让你困惑的现象现在全有了答案1️⃣为什么未初始化的局部变量是“烫烫烫”在 Debug 模式下编译器会用 0xCC 填充未初始化的栈内存。0xCC 在 GB2312 汉字编码中对应 “烫”。当你打印一个未初始化的 int 时它可能读到四个 0xCC组合起来就是“烫烫烫”。这是编译器在温柔地提醒你你没给我赋值2️⃣为什么不能返回局部变量的指针或引用因为函数返回后它的栈帧被销毁esp 恢复ebp 恢复局部变量的内存不再属于你。即使你拿到了地址那块内存随时可能被下一个函数的栈帧覆盖 —— 这就是 悬空指针/引用。3️⃣那为什么你的代码中 int b adc(); 有时候能打印出正确的 1看下面这个例子intadc(){intc1;returnc;// 返回引用地址}intmain(){intbadc();// 从该地址读取值 → 拷贝给 bcoutb;// 可能打印 1}栈帧时间线· adc 运行时c 在栈上值为 1。· adc 返回前把 c 的值放入 eax但这里返回的是引用所以返回的是地址。· adc 栈帧销毁但内存中的 1 暂时没被覆盖。· main 中 int b adc(); 去那个地址读值 → 读到了 1。这完全是运气 因为那块“已销毁”的内存还没有被其他函数占用。一旦在 adc() 和 main 之间插入别的函数调用新栈帧就可能覆盖它结果就变成随机值或程序崩溃。intadc(){intc1;returnc;}voidfoo(){intx999;}// 会覆盖原 c 的位置intmain(){intrefadc();// 悬空引用foo();// 覆盖coutref;// ❗ 未定义行为可能打印 999 或其他}一句话能打印出正确值 ≠ 代码正确。未定义行为有时“碰巧”正确但绝不能依赖。 五、一张流程图总结全过程main 调用 add 的完整生命周期 ------------------------ | main 压栈参数 (b, a) | | (从右向左) | ------------------------ │ ▼ ------------------------ | call add: 压返回地址 | | 跳转到 add 代码 | ------------------------ │ ▼ ------------------------ | add 保存 ebp设立新 | | 栈帧分配局部变量空间 | ------------------------ │ ▼ ------------------------ | 执行加法结果放入 eax | ------------------------ │ ▼ ------------------------ | add 恢复 esp、ebp | | ret 指令弹出返回地址 | ------------------------ │ ▼ ------------------------ | main 清理参数从 | | eax 读取返回值 | ------------------------️ 六、你可以亲自看一眼调试实战打开 Visual Studio或 Dev-C 带调试设置断点运行到 add 函数内部· 打开 寄存器 窗口CtrlAltG观察 ebp、esp 的变化。· 打开 内存 窗口CtrlAltM输入 ebp8 能看到参数ebp-4 能看到局部变量可能显示 0xCCCCCCCC 未初始化。你会亲眼看到那个“烫烫烫”的栈内存。✅ 写在最后函数栈帧是理解 C/C 底层执行模型的一把钥匙。初学者不必一次记住所有汇编细节但抓住这几个核心ebp 定位当前函数esp 管理栈顶。参数从右向左压栈返回地址紧随其后。局部变量在栈上分配函数结束即失效。返回值通过 eax 带回不依赖栈内存。未初始化的“烫烫烫”是调试版特有的保护填充。返回局部变量的引用/指针是未定义行为即使偶尔正确也绝不能写。当你以后再看到函数调用时脑子里应该浮现出那个精巧的栈帧结构 —— 它像一块块积木垒起来又拆掉却保证了整个程序井然有序地运行。 这就是计算机的浪漫看不见的栈撑起了看得见的一切。希望这篇文章对你有所帮助我们下篇文章见

相关文章:

【函数栈帧的创建和销毁:一文看懂 C/C++ 函数调用的底层秘密】

本文适合:被“局部变量为什么是随机值”、“函数怎么传参”、“返回值怎么带回来”这些问题困扰过的初学者。 文末会解释:为什么返回局部变量的引用有时能打印出正确值,但依然是错的?Hello,大家好呀,这里是小J,函数栈帧…...

《Enterprise Architecture with SAP》—— 从“纸上蓝图”到“场景落地”

上一篇文章(《Enterprise Architecture with SAP》— 从“项目思维”到“企业级全局视角”),我们花了不少篇幅把SAP企业架构的“骨架”搭起来了——五大支柱是什么、方法论怎么走、参考内容给什么蓝图、EA在企业里和谁配合干活。用一句话总结…...

王炸!史上最强的智慧园区管理系统,java最新技术栈,支持信创!

一、项目简介本软件是一款面向智慧园区与智慧楼宇的综合管理系统,采用先进的微服务架构(SpringCloud)、JDK 17、Spring Boot 3.2、MySQL、Vue3、Vite 和 UniApp 技术栈,支持小程序、H5、公众号、App 多端适配,前后端分…...

【扣子coze教程】0成本搭建自动生成公众号的飞书智能体(附实战工作流)

今天教大家0成本搭建自动生成公众号的飞书智能体,并部署至飞书。话不多说,咋们直接开始~ 1. 采集网站文章的工作流 如下是完整的工作流1.1 登录多维飞书表格 创建url、title、content、new_content列,为后续保存位置做准备其中url用以存放网页…...

这些坑我已经帮你踩过了,Vue3+TS 实战开发必看!

这些坑我已经帮你踩过了,Vue3TS 实战开发必看! 上周五临下班,产品突然甩过来一个“紧急需求”:把核心的数据看板模块用 Vue3 TypeScript 重构,周一早会直接给老板演示。我当时的内心是极度自信的:“Vue3 组…...

【飞机】基于matlab数据驱动的多传感器飞机健康监测系统【含Matlab源码 15551期】

💥💥💥💥💥💥💞💞💞💞💞💞💞💞欢迎来到海神之光博客之家💞💞💞&#x1f49…...

【火箭】基于matlab模拟运载火箭俯仰控制系统中基于IMU的故障检测并结合执行器动力学【含Matlab源码 15550期】含报告

💥💥💥💥💥💥💞💞💞💞💞💞💞💞欢迎来到海神之光博客之家💞💞💞&#x1f49…...

Cortex-R52学习:时钟和复位

文章目录1. 时钟和时钟使能2. 复位信号3. 复位相关信号1. 时钟和时钟使能 Cortex-R52处理器采用单一时钟驱动其所有触发器和存储器。包括复位输入在内的多种输入信号均配有同步逻辑,允许它们以异步于处理器时钟的方式工作。大多数总线都配有使能输入,使…...

【linux学习】linux工具篇(下)

Linux调试器-gdb使用,Linux项目自动化构建工具-make/Makefile我是程序员小青蛙,下面分享linux的工具利用前言程序的发布方式有两种,debug模式和release模式 Linux gcc/g出来的二进制程序,默认是release模式 要使用gdb调试&#xf…...

Pandas 数据处理进阶:缺失值、合并、分组聚合与透视表

Pandas 数据处理进阶:缺失值、合并、分组聚合与透视表 在完成 pandas 的基础操作(索引、筛选、赋值、函数应用)之后,下一步便是处理真实数据中常见的问题:缺失值、多表合并、分组统计以及数据透视。本文带你系统掌握这…...

Taotoken的TokenPlan套餐如何为个人开发者节省成本

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 Taotoken的TokenPlan套餐如何为个人开发者节省成本 对于个人开发者或小型工作室而言,在项目开发中集成大模型API是提升…...

源代码论文分享|基于 Spring Boot 的校园商铺管理系统!

很多同学选毕业设计时都会纠结:题目太简单,怕老师觉得没含金量;题目太复杂,又怕自己做不完。 其实像校园商铺管理系统这种项目,就挺适合拿来做毕设或课程设计。它有真实场景,功能也能展开,技术…...

DeepSeek LeetCode 2488. 统计中位数为 K 的子数组 public int countSubarrays(int[] nums, int k)

这道题要求统计所有子数组中,中位数等于 k 的子数组个数。 核心思路: 先找到 k 在数组中的位置 pos中位数定义(对于奇数长度):排序后中间的数 k等价转换:对于子数组,比 k 小的数个数 比 k 大的…...

源代码论文分享|社区养老服务平台的设计与实现!

有些毕业设计题目,听起来不是特别“炫”,但真的很适合做,也很容易写出实际意义。 比如这次分享的这个项目:社区养老服务平台的设计与实现。 现在社区养老、居家养老、智慧养老这些方向本身就很有现实背景,老师看到这…...

曝GPT-5.5用上“全球最快芯片”,Claude慌了

120B模型飙到2000 token/秒,CFO更放话已在跑GPT-5.5!Cerebras 560亿美元IPO首日暴涨68%,但SemiAnalysis万字拆解直指死穴。 SemiAnalysis,硅谷最硬核的芯片分析机构,4月份光是AI工具的订阅费就烧到了年化1000万美元。…...

3分钟告别Windows桌面混乱:这款免费工具让你的图标瞬间变整齐

3分钟告别Windows桌面混乱:这款免费工具让你的图标瞬间变整齐 【免费下载链接】NoFences 🚧 Open Source Stardock Fences alternative 项目地址: https://gitcode.com/gh_mirrors/no/NoFences 还在为Windows桌面上那些杂乱无章的图标头疼吗&…...

商业空间吸音地毯怎么选?16 年品牌雅尔居靠谱

商业空间装修,噪音控制是刚需。办公室人声嘈杂、酒店走廊脚步声扰客、工装大堂回音重,都会直接影响空间体验与使用效率。选对吸音地毯,既能高效降噪,又能提升空间质感,是商业空间地面材料的优选。今天就来聊聊吸音地毯…...

为AI智能体项目选择与接入高性价比大模型API服务

🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 为AI智能体项目选择与接入高性价比大模型API服务 在构建AI智能体或自动化工作流时,开发者面临的核心挑战往往集中在两个…...

进程管理器大横评:从 PM2 到 Systemd 的选型与实战

一、为什么需要进程管理器? 在服务器运维的世界里,“进程管理器”(Process Manager)是一个看似基础却极其关键的角色。它的核心使命可以概括为:确保你的应用程序在服务器重启、进程崩溃、资源耗尽等意外情况下&#xf…...

实测:JD匹配度从50%到90%,面试邀约直接翻倍,我才发现简历写错了10年!

“简历投出去就石沉大海,每天海投几十份,零回复。”“好不容易收到面试,结果聊了几句就没下文了,感觉岗位根本不适合我。”“JD看了又看,觉得自己的经验挺符合啊,为啥总是卡在第一关?”这些&…...

零经验想投运营?3分钟AI生成高匹配简历,告别海投无效!

“我一个零经验小白,简历怎么写才能打动运营岗HR啊?!!” “海投了快100份简历,一个面试都没有,我是不是不适合运营?” 相信这是大部分想转行或者刚毕业,对运营岗有点心动但完全没经…...

打卡信奥刷题(3292)用C++实现信奥题 P8976 「DTOI-4」排列

P8976 「DTOI-4」排列 题目背景 Update on 2023.2.1:新增一组针对 yuanjiabao 的 Hack 数据,放置于 #21。 Update on 2023.2.2:新增一组针对 CourtesyWei 和 bizhidaojiaosha 的 Hack 数据,放置于 #22。 构造一个排列 ppp&…...

转行简历不会衔接?AI一键生成,自然过渡无违和感,邀约率飙升3倍!

“我以前是做销售的,想转行产品经理,简历上怎么写才能不让HR觉得我风马牛不相及?” “干了几年运营,现在想尝试开发,简历里除了写熟悉Word、Excel,还能写啥?” “裸辞转行,简历一片…...

RK3588开发板Ubuntu系统深度解析:架构设计与性能优化指南

RK3588开发板Ubuntu系统深度解析:架构设计与性能优化指南 【免费下载链接】ubuntu-rockchip Ubuntu for Rockchip RK35XX Devices 项目地址: https://gitcode.com/gh_mirrors/ub/ubuntu-rockchip 在嵌入式开发领域,Rockchip RK3588处理器凭借其强…...

FlashAttention 深度解读:让大模型注意力机制“一口气算完“

FlashAttention:让大模型注意力机制"一口气算完" 想象你在厨房做菜。冰箱在远处(HBM,高带宽内存),料理台在面前(SRAM,片上缓存)。每次要切菜,都得走过去开冰箱…...

实战测试10款降AIGC软件:只选真正管用的那一款!

随着AI写作工具的普及,论文撰写和内容创作变得前所未有的高效,许多学生和职场人都从中受益。然而,随着AIGC检测技术的不断升级,越来越多的人开始面临新的挑战:原本流畅自然的AI生成内容,如今很容易被系统识…...

【期刊征稿 | 录用后最快当月见刊,刊后1个月检索,且检索稳定】第九届艺术、教育与管理国际学术会议(ICAEM 2026) - 第二期

录用后最快当月见刊,刊后1个月检索,且检索稳定 | 含ISSN号,DOI,封面目录 第九届艺术、教育与管理国际学术会议(ICAEM 2026) - 第二期 2026 9th International Conference on Arts, Education and Management 2026年…...

从入门到发烧:2026 Linux 必装 13 款播放器(VLC/MPV/Kodi 全覆盖)

Linux视频播放器选择多样,如榛名、MPlayer、VLC等,功能强大、支持多格式,满足各类用户需求 一、榛名视频播放器 榛名视频播放器是一款基于Qt的开源视频播放器,提供了许多基本功能。其特点包括支持Youtube-dl、控制播放速度、丰富…...

【案例共创】CodeArts+SKILL 双引擎:AI 驱动 WEB 服务器极速部署

本案例由开发者:JeffDing提供,华为开发者空间案例中心优化并收录。 最新案例动态,请查阅【案例共创】CodeArtsSKILL 双引擎:AI 驱动 WEB 服务器极速部署小伙伴们快来进行实操吧! 一、概述 1.1 案例介绍 华为云码道…...

如何在跨平台场景下实现高效远程桌面控制?BilldDesk Pro的现代化解决方案

如何在跨平台场景下实现高效远程桌面控制?BilldDesk Pro的现代化解决方案 【免费下载链接】billd-desk 基于Vue3 WebRTC Nodejs Flutter搭建的远程桌面控制、游戏串流 项目地址: https://gitcode.com/gh_mirrors/bi/billd-desk 在远程办公和技术支持日益普…...