第一部分 函数调用的机器级表示(x86 32 位,GCC 编译器)
一、基础概念:函数调用栈 & 栈帧
1.函数调用栈
进程运行时系统分配的内存区域,专门用于实现多层函数调用,采用栈结构,后进先出。
○程序入口:main函数,最先入栈;
○每调用一个新函数,新增一层栈帧压入栈顶;
○函数执行完毕返回,栈帧从栈顶销毁。

2.栈帧定义
每个函数独占一块栈内存,称为该函数的栈帧,存储:局部变量、调用参数、返回地址、上层栈基址、寄存器备份、对齐空闲区。

3.栈空间绘图规则(高频考点)
○32 位系统进程虚拟地址空间共 4GB:低 3GB 用户区、高 1GB 内核区;
○函数栈栈底为高地址,栈顶为低地址,画图时上方高地址(栈底)、下方低地址(栈顶);
○栈操作默认4 字节为单位,push/pop/sub/add ESP均 ±4。

4.两个核心栈寄存器(全局唯一,所有函数共用)
在这里插入图片描述

二、两条核心指令:CALL / RET(Return)
解决函数调用两大核心问题:跳转过去、执行完能回来
1.CALL 函数标号(发起函数调用)
两步操作:
① 保存返回地址:将当前 IP(指令指针,等价 PC 程序计数器)的值压栈,这是call下一条指令的地址;
② 无条件跳转:修改 IP,指向被调用函数第一条指令。
等价伪操作:push IP; jmp 目标函数

2.RET(函数返回)
一步操作:从栈顶弹出之前保存的返回地址,写入 IP,程序回到 call 的下一行继续执行。
等价伪操作:pop IP

三、栈内存的两种读写方式
方式 1:push /pop(仅操作栈顶,自动修改 ESP)
1.push 源
执行流程:ESP = ESP - 4 → 将源数据写入 ESP 指向栈单元
源支持:立即数、通用寄存器、内存单元(如EBP+8)
2.pop 目标
执行流程:取出 ESP 栈顶数据存入目标 → ESP = ESP + 4
目标支持:通用寄存器、内存单元
3.局限:只能操作栈顶,访问栈中间 / 底部数据不方便。

方式 2:MOV + EBP/ESP 偏移(灵活访问栈任意位置,复习重点)
无需改变 ESP,直接通过基址 + 偏移寻址栈内数据:
1.局部变量:存于 EBP 下方(低地址),用 \EBP-4、EBP-8、EBP-12…\ 访问
C 语言定义顺序越靠前的局部变量,偏移值越大(越靠近栈顶);
例:EBP-4是最后定义的局部变量。
2.调用参数:存于 EBP 上方(高地址),用 \EBP+8、EBP+12、EBP+16…\ 访问
EBP+8:第一个传入参数;EBP+12:第二个传入参数。
3.补充规则:MOV指令不支持两个操作数同时为主存,栈→栈必须借寄存器中转。

辅助指令:SUB / ADD 修改 ESP
•SUB ESP, N:ESP 减小,向下拓展栈帧,开辟局部变量空间;
•ADD ESP, N:ESP 增大,回收栈空间。

四、栈帧切换流程(所有非 main 函数标准汇编框架,真题必考)
1.被调用函数入口:建立新栈帧(2 条基础指令 / ENTER 简化指令)

push EBP    ; 保存上层函数栈基址(旧EBP入栈,存于当前栈帧最底部)
MOV EBP, ESP; 将当前ESP赋值给EBP,标记新函数栈帧底部

•简化指令 ENTER:一条指令等价上面两句。
•作用:保存上层栈帧边界,初始化当前函数栈基准。

2. 函数执行中间逻辑

  1. SUB ESP, 数值:开辟局部变量栈空间;
  2. 读写局部变量:MOV 寄存器, EBP-偏移 / MOV EBP-偏移, 立即数;
  3. 调用下层函数前:提前将参数写入栈帧顶部;
  4. 寄存器保护:若当前寄存器数据后续有用,push入栈备份,调用返回后pop恢复。

3. 函数出口:恢复上层栈帧(2 条基础指令 / LEAVE 简化指令)
RET 之前必须执行,还原调用者的 EBP、ESP

MOV ESP, EBP; ESP回到当前栈帧底部,回收所有局部变量空间
pop EBP     ; 弹出栈底保存的上层EBP,恢复调用者栈基址

•简化指令 LEAVE:一条指令等价上面两句。

4. RET 返回
恢复 IP,回到调用函数。

五、标准栈帧完整结构(从上到下:高地址→低地址)
1.上层函数参数区(仅本函数被调用时存在)
调用者 call 前压入,当前函数通过EBP+8、EBP+12读取;
2.返回地址(必存在)
CALL 指令自动压栈,RET 读取;
3.上层 EBP 旧值(必存在)
push EBP 存入,用于恢复调用者栈帧;
4.局部变量区(可选,无局部变量则无)
EBP 往下:EBP-4、EBP-8…;
5.寄存器备份区(可选)
调用下层函数前保存寄存器,防止数据被覆盖;
6.对齐空闲碎片(可选)
GCC 编译器要求:如果函数内部还要调用其他函数,栈帧总大小必须是 16 字节整数倍,不足则填充空闲 4 字节块;仅最内层被调用函数无此限制。

六、参数与返回值传递规则
1.参数传递(多参数)
在CALL执行前,调用者把参数写入自身栈帧顶部;
参数列表先写的参数,在栈中位置更靠近栈顶(高偏移)。

2.返回值传递(单一返回值)
统一使用EAX寄存器存储返回结果;
被调用函数计算完成后将结果放入 EAX,调用函数返回后直接读取 EAX。

七、阅读汇编代码解题模板(真题通用)

  1. 函数开头:必有push EBP + MOV EBP, ESP(或 ENTER);
  2. 中间出现EBP-4/8/12:操作局部变量;
  3. 中间出现EBP+8/12:读取外部传入参数;
  4. CALL 指令之前:一堆 MOV/PUSH 用于压入参数;
  5. RET 指令之前:必有 LEAVE(或 MOV ESP,EBP + pop EBP);
  6. 函数末尾 MOV xxx,EAX:将返回值存入 EAX。

第二部分 指令系统:CISC 复杂指令集 vs RISC 精简指令集
一、基础定义
1.CISC(复杂指令集计算机)
设计思路:提供大量、功能复杂的专用指令,一条指令完成复杂运算;
代表架构:x86(Intel/AMD 台式机、笔记本、服务器)。

2.RISC(精简指令集计算机)
设计思路:只保留最常用基础指令,复杂功能用多条简单指令组合实现;
代表架构:ARM(手机、平板、嵌入式设备)、RISC-V。

3.底层依据:80/20 规律
程序运行时 80% 时间仅使用 20% 简单基础指令,绝大多数复杂指令极少使用。

二、CISC 核心特点

  1. 指令数量庞大:通常 > 200 条,功能丰富,包含专用复杂运算指令;
  2. 指令字长不固定,长短指令混杂;
  3. 执行周期差异大:简单指令 1 周期,复杂指令数十周期;
  4. 访存限制宽松:运算指令可直接读写内存(乘法、加法能直接操作主存数据);
  5. 通用寄存器数量少:可直接访存,不需要大量寄存器中转;
  6. 控制方式:微程序控制,硬件电路复杂,成本高;
  7. 流水线实现困难:指令长度、周期不统一;
  8. 编译器优化难度大:复杂底层指令封装,无法自由拆分优化;
  9. 硬件实现:复杂指令单独电路,超复杂指令靠微程序拆解实现。

三、RISC 核心特点

  1. 指令精简:指令总数 < 100 条,仅保留基础加减、跳转、读写内存;
  2. 指令字长定长,译码速度更快;
  3. 执行效率统一:绝大多数指令单周期完成;
  4. 严格访存限制:仅 LOAD(读内存→寄存器)、STORE(寄存器→写内存)两条指令能访问主存,所有运算只能操作寄存器;
  5. 通用寄存器数量极多:运算必须先 LOAD 到寄存器,需要大量寄存器缓存数据;
  6. 控制方式:组合逻辑硬布线控制,电路简单、功耗低、速度快;
  7. 流水线天然适配:指令规格统一,流水线是标配,大幅提升 CPU 吞吐;
  8. 编译器优化友好:基础指令可自由组合,编译程序能深度优化代码;
  9. 功耗低:电路简单,适合移动端、嵌入式设备。

四、CISC & RISC 对比速记表
在这里插入图片描述

五、快速区分技巧(做题判断)

  1. 若题目中乘法 / 加法指令直接读取内存操作数 → CISC;
  2. 运算指令只能操作寄存器,读写内存单独用 load/store → RISC。

复习重点总结
1.栈帧核心:EBP 标记栈底、ESP 标记栈顶,栈高地址到底、低地址到顶;
2.CALL/RET 底层原理:压返回地址 + 跳转、弹返回地址恢复 IP;
3.函数汇编标准四段式:建立栈帧→局部变量 + 参数处理→LEAVE 恢复栈帧→RET 返回;
4.寻址记忆:EBP-偏移=局部变量,EBP+偏移=传入参数;
5.返回值统一存 EAX,参数调用前压入栈;
6.CISC/RISC 核心区分点:指令数量、访存限制、寄存器数量、流水线、控制方式。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐