🎨 颜色标记说明

  • 粉色:特别重要、主线必掌握、优先复盘的内容。
  • 蓝色:核心概念、术语、分类名。
  • 橙色:需要关注的边界、易错点、注意事项。
  • 绿色:解释类内容、帮助理解知识点。

返回:全书总览 · 上一章:表达式求值 · 下一章:函数原理

先画基本块,再猜语法

基本块是一段单入口、单出口的连续指令。控制流图以基本块为节点、跳转关系为边。

反汇编中真正存在的是比较和跳转;if、switch、for 是对控制流的高级解释。

单分支 if

1
2
if (x > 10)
work();

常见形态:条件不成立时跳过主体。

1
2
3
4
cmp eax, 10
jle short end_if
call work
end_if:

编译器也可能反转条件,因此不要把某一个跳转助记符机械翻译成源码条件。

if-else

1
2
3
4
5
    条件
/ \
分支A 分支B
\ /
汇合点

其中一个分支末尾通常需要无条件跳转,避免继续落入另一分支。

多条件与判定树

连续的 if/else if 可能表现为线性比较,也可能被优化成更平衡的判定树。恢复时应先列出每条边的条件,再组合成高级逻辑。

switch 的几种实现

线性比较

适用于 case 少、值稀疏的情况。

二分判定树

适用于值较多但分布稀疏的情况。

跳转表

适用于 case 较密集的情况:

1
2
3
4
sub eax, min_case
cmp eax, range
ja default_case
jmp dword ptr [table+eax*4]

跳转表恢复步骤:

  1. 找到归一化后的索引。
  2. 确定边界检查和默认分支。
  3. 读取表项并标记所有目标。
  4. 处理多个 case 指向同一目标的情况。

循环识别

while

先判断条件,再执行循环体,可能一次都不执行。

do-while

先执行循环体,尾部再判断,至少执行一次。

for

通常包含初始化、条件、循环体和迭代四部分,但优化后与 while 很难严格区分。

1
2
3
4
5
6
初始化 → 条件 ─失败→ 退出
│成功

循环体

迭代 ─────→ 条件

break 与 continue

  • break 指向循环出口。
  • continue 指向下一轮判定或 for 的迭代部分。

嵌套循环中,必须根据跳转目标属于哪一层循环判断。

循环优化

Release 代码可能出现:

  • 循环展开。
  • 向量化。
  • 强度削弱。
  • 循环合并或拆分。
  • 不变量外提。
  • 已知次数循环被直接展开或计算掉。

优化会让“一个回边对应一个循环”的简单判断失效。需要结合支配关系、数据变化和退出条件观察。

结构化恢复方法

  1. 标记所有基本块和跳转边。
  2. 从函数出口向前找汇合点。
  3. 识别回边和循环头。
  4. 把复杂条件拆成原子比较。
  5. 先写 goto 版伪代码,再整理成 if/loop。

学习检查清单

检查项 状态
能划分基本块并画出控制流图 待复盘
能识别 if 与 if-else 的汇合点 待复盘
能恢复跳转表的索引和范围 待复盘
能区分循环入口判断与尾部判断 待复盘
能识别 break、continue 的跳转目标 待复盘