⚙️ 第一章 对程序员来说,CPU到底是什么
📄 一句话概括

条件分支和循环在硬件上是同一件事——改写 PC:往前跳是分支,往回跳是循环。

📝 本节和前面几节的关系

1.3 说 PC 只管【下一条去哪儿】,这一节看它被改写时会发生什么。

一、条件分支:比较 + 跳转

写代码时 if 像是一种结构,落到硬件上却是两条指令合力: 比较指令:算出条件的结果,写进标志寄存器。 跳转指令:看标志寄存器,决定要不要改 PC。 条件成立 → PC 被改成目标地址;不成立 → 什么都不做,PC 正常前进。 所以【什么都不做】也是一种执行结果——这一点值得记一下,很多流程问题都出在这里。

二、循环:把 PC 拨回同一处

循环的硬件实现只有一句:在循环体末尾让 PC 跳回循环开头。 那怎么结束?靠的是【判断不成立时不再改写 PC】,让 PC 自己走出去。 用 1+2+3+4+5 走一遍:判断 6 次,前 5 次成立进循环,第 6 次不成立往下走。 所以循环的三样东西在硬件上都对得上:循环条件=比较指令,循环体=中间那几条,回边=跳转指令。

三、它们是同一种动作的两种用法

分支和循环都要【先比较、再决定改不改 PC】。 唯一的区别在跳的目标地址: 目标在前面 → 循环(回头再来一遍)。 目标在后面 → 分支(跳过一段)。 所以【无限循环】就是判断永远成立、【落到死循环出不去】就是 PC 永远被改写回原处。

四、知道这件事有什么用

一、解释性能:循环慢的一部分不在循环体,而在 PC 来回跳本身——所以减少循环次数往往比优化循环体更值。 二、解释硬件:CPU 里有【分支预测】,专门猜下一次 PC 往哪走;猜错要清空流水线,代价很大。 三、解释风格:goto 被诟病,正是因为它直接改 PC——跳出去之后,流程在人眼里就断了线。

五、动手 Demo:循环,就是把 PC 拨回前面

教材那句话:条件分支和循环,都只是改写 PC 的值。 拿 1+2+3+4+5 试一遍,看 PC 每轮落在哪儿、什么时候不回。

i, s = 1, 0
for round in range(1, 7):
    if i > 5:
        print("第 %d 轮  i=%d  6<=5 不成立  往下走(PC 去 0x0040)  sum=%d"
              % (round, i, s))
        break
    s += i
    print("第 %d 轮  i=%d  成立  进循环(PC 去 0x0020)  回边跳回 0x0010  sum=%d"
          % (round, i, s))
    i += 1

实跑输出:

--------------------------------------------------------------------
循环,就是把 PC 拨回前面
--------------------------------------------------------------------

教材那句话:**条件分支和循环,都只是改写 PC 的值。**
拿 1+2+3+4+5 试一遍,看 PC 每轮落在哪儿、什么时候不回。

  代码(右边是它所在的地址):
    sum = 0           0x0000
    i = 1             0x0005
  比较 i 与 5         0x0010   <- 每轮都回到这里
    i > 5 就跳去退出   0x0015
    sum = sum + i     0x0020
    i = i + 1         0x0030
  跳回 0x0010         0x0035   <- 回边
    (退出,往下走)    0x0040


  轮次     PC=0x0010 时 i     判断             走哪条                     算完 sum
  ---------------------------------------------------------------------------------
  1        1                  1 ≤ 5 成立       进循环(PC 去 0x0020)     1
  2        2                  2 ≤ 5 成立       进循环(PC 去 0x0020)     3
  3        3                  3 ≤ 5 成立       进循环(PC 去 0x0020)     6
  4        4                  4 ≤ 5 成立       进循环(PC 去 0x0020)     10
  5        5                  5 ≤ 5 成立       进循环(PC 去 0x0020)     15
  6        6                  6 ≤ 5 不成立     往下走(PC 去 0x0040)     15

  整个循环只有两次【改 PC】的动作:
    判断成立  -> PC 跳到循环体(0x0020),往下做
    回到底部  -> PC 跳回判断处(0x0010),再来一轮
  判断不成立那次不改 PC,让 PC 自然走到 0x0040——循环就结束了。

  **所以【循环】不是一种特殊结构,只是 PC 往回走了几次。**

整个循环只有两次【改 PC】的动作:判断成立时跳进循环体,回到底部时跳回判断处。 循环不是一种特殊结构,只是 PC 往回走了几次。

30 秒实验

  1. 写一个 for 循环,在纸上数一数它一共改了几次 PC(判断几次、回边几次)。
  2. 写一个 while True 死循环(留好 Ctrl+C 出口),体会【判断永远成立】是什么样子。

常见误区

  1. 以为 if 和 for 是两种不同的机器结构。它们都是【比较 + 跳转】。
  2. 以为循环慢只是循环体复杂。PC 来回跳本身也有代价,减少轮次常常更划算。
自测三问(点开对答案) 1. 条件分支由哪两条指令配合完成? —— 比较指令写标志寄存器,跳转指令读标志位决定是否改写 PC。 2. 循环在硬件上怎么实现? —— 在循环体末尾把 PC 改回循环开头的地址。 3. 分支和循环的区别是什么? —— 都是比较加改 PC;跳的目标在前面是循环,在后面是分支。

相关笔记