简单理解函数调用:栈帧(Stack Frame)与内存布局简单

栈帧 stack frame 函数调用栈帧复习和验证 阅读详情


前言

  先说明:不同 CPU、编译器、优化级别、操作系统 ABI 的细节不同。现代 64 位系统里,参数通常优先走寄存器,不一定真的全部压栈。但“调用栈 / 栈帧”的整体模型仍然非常重要。

1、 程序内存的大致布局

高地址
┌──────────────────────┐
│        栈 Stack       │  ← 函数调用、局部变量、返回地址等
│          ↓           │  通常向低地址增长
├──────────────────────┤
│                      │
│        空闲区         │
│                      │
├──────────────────────┤
│          ↑           │
│        堆 Heap        │  动态分配,如 malloc / new
├──────────────────────┤
│ 全局变量 / 静态变量区 │
├──────────────────────┤
│ 代码区 / 只读数据区   │
└──────────────────────┘
低地址

这里最重要的是:

  • 栈:管理函数调用过程中的临时数据。
  • 堆:动态申请的对象,生命周期通常不由函数返回直接决定。
  • 代码区:保存函数机器指令。
  • 全局/静态区:全局变量、静态变量等。

2、 什么是栈、栈帧

  栈是一种“后进先出”的内存结构。可以把它想象成一摞盘子:最后放进去的盘子,最先拿出来。
  函数调用时,当前函数使用一块栈空间,这一块空间称为:栈帧,Stack Frame。
  一个函数的栈帧严格包含:

内容说明
返回地址call 指令压入,ret 指令弹出。逻辑上属于被调用者栈帧的边界标记。
保存的帧指针(旧 EBP/FP)指向上一个函数的栈帧基址,形成帧指针链(Frame Pointer Chain)。
保存的寄存器被调用者承诺不破坏的寄存器(callee-saved),用之前先保存。
局部变量函数体内定义的变量。
编译器临时变量表达式求值、结构体复制等产生的匿名临时空间。
对齐填充满足 ABI 栈对齐要求(如 16 字节)的占位字节。

不属于当前函数栈帧,但紧密相关的内容:

内容归属说明
函数参数调用者栈帧调用者分配和清理,但被调用者可通过 EBP + 正偏移 访问。
更上层的栈帧调用链通过帧指针链可以回溯所有调用者的栈帧。

每调用一个函数,通常都会产生一个新的栈帧。例如:

main()
 └── funcA()
      └── funcB()

栈中可能是:

+------------------+
| main 栈帧        |
+------------------+
| funcA 栈帧       |
+------------------+
| funcB 栈帧       | ← 当前正在执行
+------------------+

当 funcB() 返回时:

+------------------+
| main 栈帧        |
+------------------+
| funcA 栈帧       | ← 当前恢复执行
+------------------+**

3、x86 两个关键寄存器:SP 和 BP/FP

  可以把 SP 和 BP/FP 理解成两个“指针”:

  • SP(Stack Pointer,栈指针):始终指向当前栈顶,栈一有进出它就会变。
  • BP/FP(Base Pointer / Frame Pointer,栈帧指针):指向当前函数栈帧的一个固定参考位置,通常在函数执行期间不怎么变。

   BP/FP 是当前函数栈帧的固定参考点,用来稳定定位局部变量、旧 BP 和返回地址,简单来说可以理解为:SP 就像一摞盘子最上面的那个位置,放东西、拿东西时它会一直移动;而 BP/FP 就像在当前这一层盘子旁边贴的一个固定标签,专门用来标记“这个函数自己的空间从哪里开始”。因为函数执行时可能不断压入临时数据,SP 的位置会变,如果只靠它就不容易找到局部变量;有了固定的 BP/FP,函数就能一直按固定距离找到自己的变量。

   函数调用时 SP 和 BP 的变化:

步骤操作操作功能说明SP(ESP)变化BP/FP(EBP)变化
调用前push args将函数需要的参数压入栈中,供被调用函数使用。减小(压入参数)不变
调用call func调用目标函数:先把返回地址压入栈,再跳转到函数代码处执行。减小(压入返回地址)不变
序言push ebp保存调用者原来的 EBP,避免当前函数修改后丢失。减小(保存旧 EBP)不变
序言mov ebp, esp建立当前函数的栈帧基准点,让 EBP 指向当前函数栈帧。不变设为当前 ESP
序言sub esp, N为当前函数的局部变量、临时数据等预留 N 字节栈空间。减小(分配局部变量)不变
执行...执行函数主体代码,例如计算、判断、访问局部变量或调用其他函数。可能浮动通常固定不变
返回mov esp, ebp回收当前函数分配的局部变量空间,使栈顶回到栈帧基准位置。增大到 EBP 处不变
返回pop ebp从栈中恢复调用者原来的 EBP,恢复调用者的栈帧环境。增大(弹出旧 EBP)恢复为调用者的值
返回ret从栈中取出返回地址,并跳转回调用函数的位置继续执行。增大(弹出返回地址)不变
调用后add esp, X清理调用前压入栈中的参数,释放参数占用的栈空间。增大(清理参数)不变

注意名称区别:

通用名称x86 32 位名称x86 64 位名称含义
SP(Stack Pointer)ESPRSP栈顶指针,指向当前栈顶
BP / FP(Base Pointer / Frame Pointer)EBPRBP栈帧基准指针,指向当前函数栈帧的固定参考位置

4、x86 函数调用流程演示

  前面讲了栈帧和寄存器,现在用一个完整的例子把整个调用过程串起来。假设有这样一个 C 函数:

int add(int a, int b) {
    int sum = a + b;
    return sum;
}

int main() {
    int result = add(3, 4);
    return 0;
}

  在 32 位 x86 环境下,main() 调用 add(3, 4) 时,栈的变化大致如下:

// 调用前:main 的栈帧
+------------------+
| main 局部变量     |
+------------------+
| 参数 b = 4       |  ← push 4
| 参数 a = 3       |  ← push 3
+------------------+
| 返回地址         |  ← call add 压入
+------------------+
| 旧 EBP          |  ← push ebp
+------------------+
| sum 局部变量     |  ← sub esp, N 分配
+------------------+  ← 此时 ESP 指向这里,EBP 指向旧 EBP 位置

  对应到寄存器表格中的每一步:

步骤操作说明
调用前push 4 / push 3参数从右往左压栈
调用call add压入返回地址,跳转到 add
序言push ebp / mov ebp, esp保存旧 EBP,建立新栈帧
序言sub esp, N为局部变量 sum 分配空间
执行mov eax, [ebp+8] / add eax, [ebp+12]取参数 a、b 并相加
返回mov esp, ebp / pop ebp撤销栈帧,恢复旧 EBP
返回ret弹出返回地址,回到 main
调用后add esp, 8清理两个参数

  注意:参数 a 和 b 在 add 的栈帧中位于 EBP 的上方(高地址),通过 [ebp+8] 和 [ebp+12] 访问。这就是为什么 EBP 被称 “帧指针” ——它提供了一个固定的参考点,无论 ESP 如何浮动,参数和局部变量的相对位置都是确定的。

5、STM32函数调用与栈变化

前面用 x86 视角讲了函数调用。但当把这套模型套到 STM32 上时,会发现对不上——因为 STM32 用的是 ARM Cortex-M 内核,采用 AAPCS(ARM 架构过程调用标准),它和 x86 的调用约定有本质区别。

5.1 STM32 和x86的核心差异:没有 call、没有 push ebp

  STM32 常见的内核包括 Cortex-M0、M0+、M3、M4、M7、M33 等,它们都属于 ARM Cortex-M 系列。

对比项x86ARM
代表厂商 / 产品Intel、AMD CPU(电脑 CPU)高通骁龙、苹果 M 系列、瑞芯微、STM32、ESP32 等
常见设备台式机、笔记本、服务器、Windows 电脑手机、平板、树莓派、单片机、苹果 Mac、嵌入式设备、路由器
指令集特点常被归类为复杂指令集计算机(CISC);性能强、功耗相对较高常被归类为精简指令集计算机(RISC);低功耗是主要优势
软件兼容性x86 程序通常不能直接运行在 ARM CPU 上ARM 程序通常也不能直接运行在 x86 CPU 上,需要重新编译或通过模拟/转译
厂商模式Intel、AMD 等厂商自行设计和生产 x86 CPUARM 公司主要授权架构或内核,各厂商可基于授权设计芯片
常见例子Intel i5、i7、i9;AMD Ryzen(锐龙)骁龙 8 Gen 系列、Apple M1/M2/M3、STM32 单片机

x86 和 ARM 都是 CPU 指令集架构,不是操作系统。它们规定了 CPU 可执行的指令、寄存器形式以及程序运行方式。

STM32和x86最关键的几个差异:

对比项x86(32位)STM32(Cortex-M / AAPCS)
调用指令call(自动把返回地址压栈)BL / BLX(返回地址存进 LR 寄存器,不压栈)
返回地址保存在栈上首先保存在 LR(R14)寄存器里
参数传递优先压栈优先用寄存器 R0~R3 传前 4 个参数,多余的才压栈
返回值EAXR0(64位用 R0:R1)
帧指针EBP 几乎必用FP 可选,Cortex-M 常用 R7 作 FP,且优化后经常省略
栈操作指令push / popPUSH / POP(本质是 STMDB/LDMIA,可一次压/弹多个寄存器)
栈指针ESP/RSPSP(R13)
返回指令retBX LR 或 POP {..., PC}

  一句话总结差异:x86 靠栈传参数和返回地址,STM32 靠寄存器传参数和返回地址,能不用栈就不用栈——这是为了性能,因为 MCU 主频低、访存慢。

💡 建议:想亲眼看到这些差异,可以在 Keil / STM32CubeIDE 里对着 add 函数打断点,打开 Disassembly 窗口 和 Register 窗口,单步执行时观察 SP、LR、R0~R3、R7 的变化,会比看图更直观。

5.2 Cortex-M 的关键寄存器

STM32 的 Cortex-M 内核中,函数调用和栈相关的寄存器主要有:

寄存器别名作用保存方式
R0~R3—用于传递前 4 个函数参数;返回值通常放在 R0;也可作为临时寄存器调用者保存(Caller-saved)
R4~R11—通用寄存器,常用于保存局部变量被调用者保存(Callee-saved)
R7FP常被 GCC 用作帧指针(Frame Pointer),指向当前栈帧基址,但不是硬件强制规定被调用者保存
R11FP(可选)编译器也可能使用 R11 作为栈帧指针被调用者保存
R12—临时寄存器,常用于编译器生成的临时计算调用者保存
R13SP栈指针,始终指向当前栈顶;Cortex-M 中可对应 MSP(主栈指针)或 PSP(进程栈指针)硬件自动管理
R14LR链接寄存器,执行 BL 指令调用函数时,自动保存返回地址硬件自动管理
R15PC程序计数器,指向当前或下一条将要执行的指令硬件自动管理

其中最关键的是:

  • SP:栈顶指针,压栈时向低地址移动,出栈时向高地址移动。
  • LR:函数调用返回地址通常保存在这里。
  • R0~R3:前四个参数一般直接放在寄存器里,不需要压栈。
  • R0:普通整数返回值通常放在这里。

5.3 STM32函数调用流程演示

先看同样的 C 代码在 STM32 上会怎么执行:

int add(int a, int b) {
    int sum = a + b;
    return sum;
}

int main() {
    int result = add(3, 4);
    return 0;
}

在 STM32 上(未优化)大致的汇编如下:

main:
    MOVS r1, #4            ; 第 2 个参数 b 放入 R1
    MOVS r0, #3            ; 第 1 个参数 a 放入 R0
    BL   add               ; 调用 add,返回地址写入 LR;BL 本身不自动压栈
                           ; add 返回后,结果位于 R0 中


add:
    PUSH {r7, lr}          ; 保存调用者的 R7,以及当前函数的返回地址 LR
    ADD  r7, sp, #0        ; R7 作为帧指针,固定指向当前栈帧基准位置
    SUB  sp, sp, #12       ; 分配 12 字节:a、b、sum 三个 int 局部空间

    STR  r0, [r7, #-4]     ; 将参数 a 保存到局部变量区
    STR  r1, [r7, #-8]     ; 将参数 b 保存到局部变量区

    LDR  r2, [r7, #-4]     ; r2 = a
    LDR  r3, [r7, #-8]     ; r3 = b
    ADD  r3, r3, r2        ; r3 = a + b
    STR  r3, [r7, #-12]    ; sum = a + b

    LDR  r0, [r7, #-12]    ; 将 sum 放入 R0,作为函数返回值

    MOV  sp, r7            ; 回收局部变量占用的栈空间
    POP  {r7, pc}          ; 恢复调用者的 R7;将保存的 LR 弹入 PC,返回 main

关键点对照

  1. 传参不压栈:add(3,4) 的两个参数直接放进 R0、R1,栈没动。
  2. 返回地址进 LR:BL add 把返回地址写进 LR 寄存器,而不是压栈。
  3. 序言 PUSH {r7, lr}:因为 add 内部又要调用别的函数(会覆盖 LR),或需要用 R7 做帧指针,所以在序言里把 LR 和旧 R7 一起压栈保存。如果 add 是叶子函数(不再调用任何函数),编译器连 LR 都不压栈,直接 BX LR 返回,栈完全不动!
  4. 返回用 POP {r7, pc}:一次性把栈上保存的旧 R7 弹回 R7,把返回地址弹到 PC,直接跳回去,等价于 x86 的 pop ebp + ret 合并成一步。
    TM32 上的栈变化图(调用 add 期间)
高地址
+------------------+
| main 的局部变量   |
+------------------+  ← 调用 add 前 SP 在这
| 旧 R7            |  ← PUSH {r7, lr}
+------------------+
| LR(返回地址)    |  ← PUSH {r7, lr}
+------------------+
| add 局部变量       |  ← SUB sp, #12
| (sum 等)        |
+------------------+  ← 此时 SP 指向这里,R7 指向栈帧基址
低地址

对比原文的 x86 图,最大区别:

  • 没有单独 push 的“参数 b=4 / 参数 a=3”那两行——它们在寄存器里。
  • 返回地址不是 call 自动压的——是序言里手动 PUSH 的(且只有非叶子函数才压)。

5.5 STM32 的“压栈/出栈”还有一个特殊场景:中断

  x86 讲的都是普通函数调用。STM32 有一个非常特殊的机制——硬件自动压栈(异常入栈):
  当中断/异常发生时,Cortex-M 内核硬件自动把 8 个寄存器压入栈:

寄存器名称简要作用异常发生时保存的内容
xPSR程序状态寄存器保存处理器当前状态,例如条件标志位、中断状态和当前执行状态异常发生前的程序状态
PC(R15)程序计数器保存当前正在执行或下一条将要执行的指令地址异常返回后继续执行的位置
LR(R14)链接寄存器普通函数调用时通常保存函数返回地址异常发生前 LR 中的原始值
R12临时寄存器供编译器或程序临时计算使用异常发生前 R12 中的数据
R3通用寄存器可用于传参、计算和保存临时变量异常发生前 R3 中的数据
R2通用寄存器可用于传参、计算和保存临时变量异常发生前 R2 中的数据
R1通用寄存器常用于传递第 2 个函数参数,也可保存临时数据异常发生前 R1 中的数据
R0通用寄存器常用于传递第 1 个函数参数和保存函数返回值异常发生前 R0 中的数据

  这称为 “自动保存的栈帧(stacked frame)”。中断返回时(执行特殊值 EXC_RETURN 的 BX LR)硬件又自动弹出这 8 个寄存器。这是 STM32 独有的、程序员看不见的“自动压栈/出栈”,x86 完全没有对应概念(x86 中断压栈规则完全不同)。

如果开了 FPU(如 STM32F4/F7),还会额外自动压 S0~S15 + FPSCR,栈帧更大。

5.6 MSP 和 PSP:STM32 有两个栈指针

x86 中通常可以简单理解为只有一个主要栈指针;而 Cortex-M 中有两个栈指针:

栈指针全称常见用途
MSPMain Stack Pointer,主栈指针复位后默认使用;异常和中断处理程序必须使用 MSP
PSPProcess Stack Pointer,进程栈指针可供线程模式下的任务使用,常见于 RTOS

在没有 RTOS 的普通裸机 STM32 工程中:

  • 程序复位后通常使用 MSP;
  • main() 和普通函数一般也都使用 MSP;
  • 因此大多数初学阶段可以把 SP 理解为 MSP。

在 FreeRTOS、RTX 等 RTOS 中,常见情况是:

  • 任务代码:通常使用 PSP;
  • 中断服务程序:使用 MSP;
  • 每个任务通常有自己的任务栈。

6. 总结

  回顾全文,从程序内存布局出发,认识了栈、堆、代码区、全局/静态区各自的分工;接着理解了栈和栈帧的概念,以及 SP、BP/FP 两个关键寄存器的作用。在 x86 架构下,通过 add(3, 4) 的完整示例,看到了参数压栈、call 压返回地址、序言建立栈帧、执行、返回撤销栈帧并清理参数的完整流程。

  随后切换到 STM32(ARM Cortex-M 架构),发现调用模型发生了本质变化:x86 是"栈优先",STM32 是"寄存器优先"。前 4 个参数直接走 R0~R3 寄存器,返回地址存进 LR 而不是压栈,叶子函数甚至可以完全不动栈。同时,STM32 还有两个独特机制:中断时硬件自动压入 8 个寄存器的异常栈帧,以及 MSP/PSP 双栈指针在 RTOS 中的分工。

  虽然 x86 和 ARM 在实现载体上不同——一个靠栈、一个靠"寄存器 + 栈"的组合——但"栈帧 / 调用栈"的整体逻辑模型是通用的。理解了这套模型,无论是排查段错误、分析递归、阅读反汇编,还是调试嵌入式中断,都能做到心中有数。


  无论选择哪条路,肯定都会对没走的那条路念念不忘吧。所以这世上没有毫不后悔的选择,而且生活也是没有正确答案的,只要坚信选择的道路就是正确答案,并把它变成正确答案就可以了。

C语言函数栈帧全解析:从内存布局到GDB调试 C语言中函数调用时的内存管理是很多开发者困惑的根源。程序运行时,栈区与堆区承担不同职责,栈区由编译器自动管理,用于存放局部变量与返回地址,其分配速度极快。每次函数调用都会在栈上创建独立的栈帧,记录参数、局部变量、返回地址等关键信息。理解栈帧布局是排查疑难bug的基础,例如数组越界破坏返回地址、递归过深导致栈溢出等。通过GDB调试工具可以直观观察栈帧的创建与销毁过程,甚至看到返回地址的压栈与弹出。掌握这些原理,能帮助开发者写出更健壮的C代码,并快速定位段错误等运行时问题。 阅读详情

相关推荐

stack frame栈帧简介(逆向工程角度)

0x01 栈帧是什么 “IA32 programs make use of the program stack to support procedure calls”. “IA32程序使用程序栈帧来支持子程序调用”(出自CSAPP,即《深入理解计算机系统》) 个人理解,栈帧就是内存的一段数据,在内存栈区域,对子程序的数据进行一个临时保存。 什么是栈? 栈和堆类似,都是操作系统中分配给用

jmp esp 3870

GTweak 完整反馈指南:报 Bug、提建议、找作者的最快路径

GTweak 是一款免安装的 Windows 系统优化工具,负责激活管理、隐私设置、系统精简和日常维护。用着顺手是好事,但一旦遇到闪退、功能失效,或者心里冒出"要是能加个 XX 功能就好了"的念头,别憋着——下面按场景教你怎么快速找到作者 **Greedeks** 并让反馈真正被处理。 [![GTweak 社区支持界面预览](https://raw.gitcode.com/GitHub_Tren

gitblog_00098的博客 236

Fluent实战——残差震荡分析与收敛优化策略

本文深入探讨了Fluent仿真中残差震荡的成因与优化策略。残差震荡是流场物理或数值问题的关键信号,文章系统分析了网格质量、边界条件、物理模型非定常性及求解设置四大主因,并提供了库朗数与松弛因子的调参心法,以及针对超音速流、共轭传热等典型场景的实战优化方案,旨在帮助用户有效提升计算收敛性与结果可靠性。

w1x2y3的博客 313

ARM64架构栈帧回溯

ARM64架构栈帧回溯简介以及demo演示

小立仔 3151

ENU坐标系实战:5分钟搞定无人机导航中的站心坐标转换

本文针对无人机导航中GPS坐标与视觉感知不匹配的定位漂移问题,提供了基于ENU坐标系的实战解决方案。通过Python代码演示了如何快速、精准地实现地心地固坐标与站心坐标之间的转换,并详细解释了转换原理、精度对比及工程优化技巧,帮助开发者从根本上解决局部相对定位不准的难题。

mqtt6iot的博客 552

实战分享:如何用Java调用Doris FE接口实现高效Stream Load数据导入(附完整代码)

本文详细介绍了如何通过Java调用Doris FE接口实现高效的Stream Load数据导入,包含完整代码示例和性能优化技巧。Stream Load作为Doris的高效数据导入方式,相比传统JDBC提升5-10倍速度,适用于大规模数据处理场景,帮助开发者解决高吞吐量数据导入挑战。

weixin_29304985的博客 416

物奇5007crash问题dump、栈回溯的四种定位方法

物奇属于RISC-V架构(chapter1_riscv.md · 华中科技大学操作系统团队/pke-doc - Gitee.com),在遇到crash问题时,系统内部会对内存进行相关的保护和检测,当发现异常时会主动调用 IOT_ASSERT,通常会产生相关的 log 文件, 如: ***.c:xxx Asserted!出现异常时的现场 dump 记录,将通用的 32 个 CPU 寄存器 dump 输出。回栈信息记录。出现异常时的堆栈信息以及调用函数地址。

里昂的博客 995

4.图像sensor的特性和驱动解析

例子:sensor驱动自己提供操作曝光时间的函数和获取曝光时间的函数,然后将这个函数注册到ISP库和3A算法库,3A算法库根据外部光照强度计算出一个Sensor曝光时间的设置值,ISP库得到这个设置值,然后通过Sensor之前注册的回调函数,把这个值设置到Sensor的硬件寄存器中去,实现整个过程。是为 ISP 配置正确的输入时序。(1)修改驱动源码(sambashar\Hi3518E_SDK_V1.0.3.0\package\mpp\component\isp\sensor\ar0130)

weixin_53762042的博客 3565

Arm汇编学习笔记(六)——函数调用栈空间以及fp寄存器

Arm上函数调用的规则在ARM System Developer's Guide文档中的ATPCS部分有详细的定义,这里主要通过函数调用过程中函数栈的情况来说明fp和sp等寄存器的作用。有关ATPCS的详细内容可以去文档中看。 fp叫做frame pointer寄存器,即栈帧指针寄存器;sp叫做stack pointer寄存器,即栈指针寄存器。那么它们具体的作用是什么呢? 首先,大家知

beyond702的专栏 2万+

实用工具系列 - Xshell安装下载与使用

一、介绍 二、下载 三、安装 四、使用

Passerby_Wang的博客 1万+

CVPR2023新作HAT实战:5分钟搞定图像超分辨率重建(附代码)

本文介绍了CVPR2023新作HAT模型在图像超分辨率重建中的实战应用。HAT通过创新的混合注意力机制,显著提升了图像重建质量,能激活更多有效像素并减少伪影。文章提供了从环境配置到模型部署的完整指南,帮助开发者在5分钟内快速实现图像超分辨率重建,并附有代码示例和性能优化技巧。

weixin_29062613的博客 236

函数堆栈sp,fp 堆栈溢出原理

函数调用栈理解 https://blog.youkuaiyun.com/wanna_wsl/article/details/70549135 C语言函数调用栈浅析 https://blog.youkuaiyun.com/huangkangying/article/details/37991445 C语言函数调用栈(一) https://blog.youkuaiyun.com/lcathm/article/details/10...

happylzs2008的专栏 831

YOLOv8剪枝 l1剪枝测试精度仅掉0.8个点,速度提高10.7fps!

文章摘要: 本文提出了一种基于组归一化的重要性评分方法GroupNormImportance,用于神经网络剪枝中的通道选择。该方法支持多种变体,包括标准L1范数、组L1范数和BN缩放因子等计算方式。核心实现包括三个关键部分:1)支持p范数计算(默认为L2);2)提供7种组重要性归约方法(如均值、最大值等);3)提供8种归一化方案(包括标准化、高斯化、LAMP等)。该方法输入一个剪枝组,输出各通道的重要性分数,通过依赖图可实现结构化剪枝。实验对比图展示了剪枝前后的效果差异,验证了该方法的有效性。

FJN110的博客 397

浅谈函数栈帧(Stack Frame)

带你快速理解函数栈帧

阿润菜菜的博客 4690

YOLO11涨点优化:loss优化 | 一种新的基于Wasserstein距离的小目标检测评估方法,助力小目标涨点

提出了一种新的基于Wasserstein距离的小目标检测评估方法,通过它们对应的高斯分布计算它们之间的相似性

①答疑群聊服务;②YOLO大模型知识问答系统;③计算机视觉论文生成智能体; 1015

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

编程底层概念回顾:虚拟内存、栈、栈帧、堆

本文回顾了C++中堆、栈和栈帧的核心概念,为理解智能指针和对象生命周期管理奠定基础。文章首先介绍了操作系统的虚拟内存机制,解释了32位和64位系统的内存空间布局。重点剖析了栈和堆的区别:栈用于自动管理函数调用和局部变量,遵循后进先出原则;而堆需要程序员手动管理动态内存分配。通过调用栈和栈帧的类比,详细说明了函数执行时内存的分配和释放机制。最后以A* a = new A()为例,揭示了对象创建过程中栈指针变量与堆内存分配的关系。这些底层知识对掌握C++内存管理至关重要,特别是与Java等自动内存回收语言形成鲜

Laurence的技术博客 4312

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

【编译器】栈帧(Stack Frame)

栈帧(Stack Frame)是栈上的内存片段,用来实现函数调用。其主要作用有:为局部变量和临时变量分配空间,传递参数,保存返回地址。 每个函数在栈上拥有独立的帧,这些帧是在函数调用时分配的。在任一时刻,寄存器ebp(帧指针)都指向帧的起始处,寄存器esp(栈指针)指向帧的结尾处,它们之间的部分是当前函数的栈帧。我们可以这样认为,ebp是局部的,作用于当前帧,而esp是全局的,作用于整个栈。

老和山之子 4458

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

揭秘 C 语言函数调用的『战争』:深入理解栈帧 (Stack Frame)

这篇技术文章通过战争比喻生动讲解了C语言函数调用中的栈帧机制。文章从程序内存中的栈区开始,以main函数调用add函数为例,详细描述了栈帧的创建过程:实参传递、返回地址压栈、新栈帧开辟。解释了函数执行时如何在自己的栈帧内工作,以及返回时如何销毁栈帧并恢复调用现场。通过这种比喻,文章阐明了局部变量作用域、递归风险等核心概念,并指出理解栈帧机制对掌握指针、参数传递等C语言特性至关重要。最后总结函数调用的三个关键阶段:开战(调用)、战斗(执行)和凯旋(返回)。

Web安全工具库 683

函数栈帧的创建和销毁

函数栈帧的创建和销毁

2201_75366661的博客 896

ida_frame 栈帧操作完全指南:IDA Pro 函数栈帧、栈变量与寄存器变量操控实战

`ida_frame` 是 IDAPython 中专门负责函数栈帧(stack frame)操作的核心模块,涵盖帧的创建与删除、栈变量的定义与类型修改、寄存器变量(register variable)管理以及栈指针(SP)变化点的追踪。本文将围绕 [skills/idapython/docs/ida_frame.md](https://link.gitcode.com/i/f6e6e5e5c59f

gitblog_00445的博客 244

JVM系列之栈帧(Stack Frame)结构

栈帧是运行时数据区中虚拟机栈的栈元素,用于支持虚拟机进行方法调用和执行的数据结构,栈帧中储存着局部变量表、操作栈、动态链接、返回地址以及一些额外的附加信息。

WZH577的博客 1067

深入理解Visual C++栈结构:从内存布局到std::stack实战

栈是计算机科学中基础的数据结构,其核心原理是后进先出(LIFO)的访问方式,直接映射到现代计算机体系结构的内存管理机制。在底层硬件和操作系统的支持下,栈通过栈指针寄存器实现高效的内存分配与释放,是函数调用、局部变量存储和程序控制流管理的基石。从技术价值看,理解栈机制对于优化程序性能、排查内存错误和掌握编译器行为至关重要,尤其在Visual C++开发环境中,栈与函数调用约定、异常处理和多线程安全紧密相关。在应用场景上,栈不仅体现在运行时内存管理,也通过C++标准库的std::stack容器为开发者提供高级抽

weixin_33981932的博客 534

CPython 帧(Frame)机制深度解析:从 _PyInterpreterFrame 到栈分配、基帧与性能剖析支持

CPython 的每次 Python 函数调用都会产生一个"帧"(frame)作为其激活记录(activation record)。本文以 CPython 官方内部文档 [InternalDocs/frames.md](https://link.gitcode.com/i/6d2959b2ef3a19a4e09f2db2fda3c8dd) 为骨架,结合源码剖析帧的三个组成区段、栈式分配与布局设计、

gitblog_00122的博客 507

函数栈帧详解:从内存四分区到函数调用全过程

在C语言等系统编程中,内存管理是理解程序运行的核心基础。从虚拟地址空间的分区来看,代码段、数据段、堆区与栈区各司其职,其中栈区因自动分配与回收的特性,天然承载了函数调用的上下文信息。函数栈帧作为一次调用在栈上划定的独立区域,保存着参数、局部变量、返回地址等关键数据,其分配与销毁过程直接决定了程序的正确性与稳定性。理解栈帧的生长方向、布局与生命周期,不仅能够解释递归、局部变量失效等基本现象,更能帮助开发者规避返回局部变量地址、缓冲区溢出、栈溢出等经典陷阱。掌握这一概念,辅以GDB等工具的实战验证,能显著提升内

weixin_34396103的博客 400

栈和栈帧

http://blog.chinaunix.net/uid-16459552-id-3257539.html  一个码农要是没遇见过coredump,那他就是神仙了。core file(coredump的转储文件)中保存的最重要内容之一,就是函数的call trace。还原这部分内容(栈回溯),并与原代码对应上,尽快找出程序崩溃的位置和原因,是码农们一生的责任。当然,你如

easier_life的专栏 992

Go: 深入理解程序调用栈与栈帧

程序调用栈是一种数据结构,用于管理函数调用过程中的活动记录。每当程序调用一个函数时,系统会将当前的执行状态保存到调用栈中,并在函数返回时从调用栈中恢复之前的状态。调用栈以“后进先出”(LIFO, Last In First Out)的方式管理这些记录,这意味着最后被调用的函数会最先完成执行。栈帧是调用栈中的基本单元,每个函数调用都会在调用栈中创建一个新的栈帧。栈帧保存了函数执行所需的所有信息,包括局部变量、返回地址、参数等。返回地址:记录函数返回时程序应继续执行的地址。参数区:存储传递给函数的参数。

十年运维开发经验的王义杰在此分享自己的知识和经验 1118
上一篇: ESP32------开发环境配置(VS Code + ESP-IDF )
下一篇: 简单理解STM32内存分配与堆栈(上)
泰然码事
博客等级 码龄5年 427粉丝 15原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值