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

循环在汇编里只有一件事:把指令指针拨回去——它不是什么新东西,只是【比较 + 按条件跳回去】而已。

📝 本节和前面几节的关系

10.7 到 10.9 讲的是函数相关的机制,这一节回到第一章那个问题:循环在硬件上到底长什么样。

一、循环的全部秘密就一句

程序在硬件上是一条一条按顺序走的(1.3 讲过),走的位置由程序计数器记着。 循环做的事只有一件:在循环体末尾,让程序计数器跳回循环开头。 就这么简单——往前跳是分支,往回跳是循环(1.4 讲过)。 所以看汇编里的循环,只要找到那条【往回跳】的指令,循环的范围就确定了。

二、一个求和循环的五条指令

求 1 + 2 + 3 + 4,写成汇编是这样: movl $0, %eax——累计值放 eax,先清零。 movl $1, %ecx——计数器放 ecx,从 1 开始。 loop: 标签下面四条:加当前的数、计数器加 1、跟 4 比一比、不超就跳回去。 整个循环体只有四条指令,核心是最后那条跳转。

三、逐圈看它是怎么停下来的

第 1 圈:eax 从 0 加到 1,ecx 变成 2,2 小于等于 4,跳。 第 2 圈:eax 变成 3,ecx 变成 3,还跳。 第 3 圈:eax 变成 6,ecx 变成 4,仍然跳。 第 4 圈:eax 变成 10,ecx 变成 5,这次不比 4 小,不跳了。 循环就停在这里——四圈下来 eax 正好是 10。

四、停下来的条件从哪儿来

cmpl $4, %ecx 自己不跳、也不改变流程——它只负责把比较结果记进标志位。 真正决定去哪儿的是后面那条 jle loop——它读标志位,决定跳不跳。 同一套机制,跳的方向不同,就成了循环或者分支(10.11 接着讲)。 所以循环没什么神秘的:它就是【条件不成立就不往回跳】。

五、动手 Demo:循环的工作原理

循环在汇编里只有一件事:把指令指针拨回去。 求 1 + 2 + 3 + 4,逐圈看它每一圈做了什么、什么时候停。

prog = [
    ("movl", "$0", "%eax"),
    ("movl", "$1", "%ecx"),
    ("label", "loop"),
    ("addl", "%ecx", "%eax"),
    ("incl", "%ecx"),
    ("cmpl", "$4", "%ecx"),
    ("jle", "loop"),
]
vm = VM(prog)
vm.run()
print("循环结束时的和", vm.reg["eax"])
eax, ecx, rnd = 0, 1, 0
while True:
    rnd += 1
    eax += ecx
    ecx += 1
    print("第 %d 圈  eax=%d  ecx=%d  还跳吗=%s" % (rnd, eax, ecx, ecx <= 4))
    if ecx > 4:
        break

实跑输出:

--------------------------------------------------------------------
循环的工作原理
--------------------------------------------------------------------

**循环在汇编里只有一件事:把指令指针拨回去。**

  求 1 + 2 + 3 + 4 的和,写成汇编是这样:

        movl $0, %eax      ; 累计值放 eax,先清零
        movl $1, %ecx      ; 计数器放 ecx,从 1 开始
    loop:
        addl %ecx, %eax    ; 加上当前的数
        incl %ecx          ; 计数器加 1
        cmpl $4, %ecx      ; 跟 4 比一比
        jle loop           ; 还没超过 4 就跳回 loop

  圈     加之前 eax     本圈加的 ecx     加完 eax     incl 后 ecx     ecx ≤ 4 吗     jle 跳吗
  --------------------------------------------------------------------------------------------
  1      0              1                1            2               是             跳
  2      1              2                3            3               是             跳
  3      3              3                6            4               是             跳
  4      6              4                10           5               否             不跳

    循环结束时的和      10
    循环走了几圈        4 圈
    循环体执行了        4 次

  **看清最后一行:这一圈的 jle 没跳,循环就结束了。**
  所以循环不是什么新东西——**它只是【比较 + 按条件跳回去】而已。**

实测:四圈之后 eax = 10,ecx = 5;最后一条 jle 没跳,循环就此结束。 循环不是什么新东西——它只是【比较 + 按条件跳回去】而已。

30 秒实验

  1. 把比较的值从 4 改成 10,算一算循环会走几圈、和是多少。
  2. 想一想:如果循环体里忘了让计数器加 1,会发生什么。

常见误区

  1. 以为循环是硬件里的一种专门结构。它就是往回跳。
  2. 以为循环结束是【自动判断】的。是跳转指令这一次没有跳。
自测三问(点开对答案) 1. 循环在硬件上是怎么实现的? —— 在循环体末尾让程序计数器跳回循环开头,不往回跳就走出循环。 2. 1 + 2 + 3 + 4 这个循环一共走了几圈? —— 四圈,eax 依次变成 1、3、6、10,最后 ecx 变成 5,条件不成立而退出。 3. 谁负责决定跳不跳? —— 跳转指令:它读取比较指令写下的标志位,决定是否改动程序计数器。

相关笔记