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

movl 是汇编里出现次数最多的指令,它只做一件事:把数据搬个地方——但它最常用,因为算、比、跳都得先把数据搬到寄存器里。

📝 本节和前面几节的关系

10.4 学会了读一行,这一节开始逐条认识指令——从出现最多的那条开始。

一、它只做一件事

movl 就是把数据从一个地方搬到另一个地方,不算、不比、不跳。 搬的两头可以是立即数、寄存器、内存的任意组合(10.4 讲的四种来源)。 这看起来太朴素了,但它是汇编里出现次数最多的指令。 原因也朴素:搬是其他所有动作的前置动作。

二、跑几条看数据流动

movl $5, %eax——5 进 eax,eax 还是 5。 movl %eax, %ebx——eax 的值复制给 ebx,eax 自己不变。 movl $100, _total——把 100 写进那个固定的内存位置。 movl _total, %ecx——再从内存读回来。 最后 addl %ebx, %ecx——ecx 变成 5 + 100 = 105。 整串跑下来,数据怎么流的、谁被改了,全都看得见。

三、三件从数据流里看出来事

movl 只搬不算——5 搬到 eax,还是 5,一点没变。 搬运方向是先源后目的地——movl %eax, %ebx 是把 eax 的值给 ebx。 寄存器可以反复被覆盖——eax 搬出去之后自己不变,但下一次写它就变了。 理解这三条,后面所有指令都只是在它们之上加一点花样。

四、为什么它最常用(回到第一章)

第一章讲过(1.7):CPU 只会【搬、算、比、跳】四件事。 而算、比、跳都要先把数据搬到寄存器里——搬是它们共同的第一步。 所以一段程序里,重复代码的其余三件事,各自前面都站着一条 movl。 这也解释了一个常见现象:优化得很好的代码,往往指令条数没少,少的是搬的次数。

五、动手 Demo:最常用的movl指令

movl 只做一件事:把数据搬个地方。 跑几条看数据是怎么流动的。

prog = [
    ("movl", "$5", "%eax"),
    ("movl", "%eax", "%ebx"),
    ("movl", "$100", "_total"),
    ("movl", "_total", "%ecx"),
    ("addl", "%ebx", "%ecx"),
]
vm = VM(prog)
for i, (shown, before, after) in enumerate(vm.run(), 1):
    print(i, shown, after["eax"], after["ebx"], after["ecx"])

实跑输出:

--------------------------------------------------------------------
最常用的movl指令
--------------------------------------------------------------------

**movl 是汇编里出现次数最多的指令**——它只做一件事:把数据搬个地方。

  跑几条 movl,看数据是怎么流动的:

  步     指令                  执行后 eax     执行后 ebx     执行后 ecx
  ----------------------------------------------------------------------
  1      movl $5, %eax         5              0              0
  2      movl %eax, %ebx       5              5              0
  3      movl $100, _total     5              5              0
  4      movl _total, %ecx     5              5              100
  5      addl %ebx, %ecx       5              5              105

  三件事从这几行里就能看出来:
    **movl 只搬不算**——5 搬到 eax,还是 5。
    **搬运方向是先源后目的地**——movl %eax, %ebx 是把 eax 的值给 ebx。
    **_total 那个字节,最后变成了 100**——而 ecx 拿的是 5 + 100。

  为什么它最常用?**因为 CPU 只会【搬、算、比、跳】四件事(1.7 讲过),**
  而算、比、跳都要先把数据搬到寄存器里——**搬是其他三件事的前置动作。**

实测五步之后:eax=5、ebx=5、ecx=105,_total 那个位置是 100。 movl 只搬不算;搬是算、比、跳的前置动作——所以它出现得最多。

30 秒实验

  1. 把 movl %eax, %ebx 反过来写成 movl %ebx, %eax 跑一遍,看谁被改了。
  2. 数一数你随手写的一小段代码,大致要搬多少次数据。

常见误区

  1. 以为 movl 会顺手做转换或校验。它只搬,不算也不查。
  2. 以为搬的方向是从左到右。先写源,后写目的地。
自测三问(点开对答案) 1. movl 是做什么的? —— 把数据从一个位置搬到另一个位置,不算、不比、不跳。 2. 为什么它是出现次数最多的指令? —— 因为算、比、跳都要先把数据搬进寄存器,搬是它们共同的前置动作。 3. movl %eax, %ebx 之后 eax 变了吗? —— 没变——movl 是复制,源端的值不会因为搬走而消失。

相关笔记