条件分支和循环在硬件上是同一件事——改写 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 秒实验
- 写一个 for 循环,在纸上数一数它一共改了几次 PC(判断几次、回边几次)。
- 写一个
while True死循环(留好 Ctrl+C 出口),体会【判断永远成立】是什么样子。
常见误区
- 以为 if 和 for 是两种不同的机器结构。它们都是【比较 + 跳转】。
- 以为循环慢只是循环体复杂。PC 来回跳本身也有代价,减少轮次常常更划算。
自测三问(点开对答案)
1. 条件分支由哪两条指令配合完成? —— 比较指令写标志寄存器,跳转指令读标志位决定是否改写 PC。 2. 循环在硬件上怎么实现? —— 在循环体末尾把 PC 改回循环开头的地址。 3. 分支和循环的区别是什么? —— 都是比较加改 PC;跳的目标在前面是循环,在后面是分支。- ☐ 下一节(1.5 函数调用的原理)
相关笔记
- 决定程序流程的程序计数器 — PC 只管下一条去哪儿
- 函数调用的原理 — 跳走之后怎么还能回得来
- 程序的流程分为三种 — 顺序、循环、分支三件套
- 🔵 计算机基础 · 索引 — 硬件底层的入门课
- ⚙️ 程序是怎样跑起来的 · 索引