📄 一句话概括
条件分支只两件事:先比较,再按结果跳——比较指令只写标志位不改变流程,跳转指令读标志位才真正改变流程。
📝 本节和前面几节的关系
10.10 讲的是往回跳,这一节讲往前跳——同一个机制的另一半。
一、两条指令,两个分工
cmpl $10, %eax——比较,结果只记进标志位。
jle small——小于等于 10 就跳去 small 那段。
注意 cmpl 自己不跳、也不算数——它只负责【记一笔】。
真正决定去哪儿的是后面那条跳转指令。
这个分工是理解所有条件逻辑的钥匙:比较负责判断,跳转负责行动。
二、用三个值各试一遍
eax 是 5:比 10 小,跳——走去 small 那段。 eax 是 10:正好相等,也跳——走去 small 那段。 eax 是 20:比 10 大,不跳——继续往下走,去了 big 那段。 三个值的三种结果,正好把【小于、等于、大于】三种情况覆盖完整。
三、这一切在第一章已经说过
第一章讲过(1.4):分支和循环在硬件上是同一件事——改写程序计数器。 并且把它拆成了两句话:比较指令算出条件结果、写进标志寄存器;跳转指令看标志寄存器、决定要不要改程序计数器。 这一节就是把那两句话落到具体指令上。 所以看书时觉得【理所当然】的一句话,展开来就是硬件上的两条指令。
四、循环和分支的唯一差别
跳的目标在前面(往回跳)→ 循环。 跳的目标在后面(往前跳)→ 分支。 除了方向,两者用的是完全相同的两条指令。 这也解释了为什么第一章说【它们是同一件事】——硬件上确实只有一套机制。
五、动手 Demo:条件分支的工作原理
条件分支也只用两条:先比较,再按结果跳。 拿三个不同的值各跑一遍,看跳不跳。
for v in (5, 10, 20):
vm = VM([("cmpl", "$10", "%eax"), ("jle", "small")],
regs={"eax": v})
vm.step()
take = vm.zf or vm.sf
print("eax=%2d cmp 之后 zf=%s sf=%s jle %s"
% (v, vm.zf, vm.sf, "跳" if take else "不跳"))
实跑输出:
--------------------------------------------------------------------
条件分支的工作原理
--------------------------------------------------------------------
条件分支也只用两条:**先比较,再按结果跳。**
cmpl $10, %eax ; 比较,结果只记进标志位
jle small ; 小于等于 10 就跳去 small
**注意 cmpl 自己不跳、也不算数**——**它只负责【记一笔】。**
真正决定去哪儿的是后面那条跳转指令。
拿三个不同的值各跑一遍,看跳不跳:
eax 的值 cmp 之后:相等吗 cmp 之后:更小吗 jle 跳了吗 结果分支
-------------------------------------------------------------------------------
5 否 是 跳了 small
10 是 否 跳了 small
20 否 否 没跳 big
这正好对上 1.4 讲的两句话:
**比较指令只写标志位,不改变流程。**
**跳转指令读标志位,才真正改变流程。**
而循环和分支的差别,**也只在【跳的目标在前面还是后面】:**
**往回跳(目标在前面)→ 循环。**
**往前跳(目标在后面)→ 分支。**
实测:eax=5 跳、eax=10 跳、eax=20 不跳。 比较只写标志位,跳转才改变流程——这正是第一章讲解的那两句话。
30 秒实验
- 把 jle 换成 jg,想一想三个值的结果会怎么变。
- 写一段真实的 if 语句,编译出汇编,找一找比较和跳转分别在哪一行。
常见误区
- 以为比较指令会改变程序流程。它只写标志位。
- 以为分支和循环是两套机制。它们共用同一对指令,只差跳的方向。
自测三问(点开对答案)
1. 条件分支由哪两条指令配合完成? —— 比较指令写标志位,跳转指令读标志位决定是否改变程序计数器。 2. 为什么说比较指令不改变流程? —— 它只把结果写进标志寄存器,程序计数器仍按顺序前进。 3. 循环和分支在硬件上的差别是什么? —— 只差跳转方向:往回跳是循环,往前跳是分支。- ☐ 下一节(10.12 体验汇编语言的意义)
相关笔记
- 条件分支和循环的原理 — 这两句话的出处
- 决定程序流程的程序计数器 — 被改的是谁
- 循环的工作原理 — 往回跳的那一半
- 🔵 计算机基础 · 索引 — 硬件底层的入门课
- ⚙️ 程序是怎样跑起来的 · 索引