⚙️ 第十章 通过汇编语言认识程序的真面目
📄 一句话概括

条件分支只两件事:先比较,再按结果跳——比较指令只写标志位不改变流程,跳转指令读标志位才真正改变流程。

📝 本节和前面几节的关系

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 秒实验

  1. 把 jle 换成 jg,想一想三个值的结果会怎么变。
  2. 写一段真实的 if 语句,编译出汇编,找一找比较和跳转分别在哪一行。

常见误区

  1. 以为比较指令会改变程序流程。它只写标志位。
  2. 以为分支和循环是两套机制。它们共用同一对指令,只差跳的方向。
自测三问(点开对答案) 1. 条件分支由哪两条指令配合完成? —— 比较指令写标志位,跳转指令读标志位决定是否改变程序计数器。 2. 为什么说比较指令不改变流程? —— 它只把结果写进标志寄存器,程序计数器仍按顺序前进。 3. 循环和分支在硬件上的差别是什么? —— 只差跳转方向:往回跳是循环,往前跳是分支。

相关笔记