编程就是把你想让电脑做的事,拆成一份 CPU 听得懂的指令清单。CPU 只会百来条基本动作、忠实地逐条执行,所以编程的真正功夫不在"写代码",而在"把大问题拆碎"。
1. 一个反常识的起点:CPU 其实很笨
前三篇我们造出了加法器(03),知道了晶体管怎么变成芯片(04)。现在把镜头对准芯片里真正的"决策者"——CPU(中央处理器),你会发现一个意外:
它不会思考,不会理解,甚至不会变通。它只会执行事先设计好的一套动作。
这套动作的总和叫指令集(Instruction Set),总共就百来条,比如:
| 动作类型 | 例子 | 大白话 |
|---|---|---|
| 数据搬运 | MOV |
"把这个数搬到那儿" |
| 算术运算 | ADD、SUB |
"加一下"、"减一下" |
| 逻辑运算 | AND、OR、XOR |
03 篇讲过的那些门 |
| 比较 | CMP |
"这两个数一样吗?" |
| 跳转 | JMP、JZ |
"下一步不按顺序走了,跳到第 N 条" |
就这么点词汇量。程序再庞大——微信有几千万行代码——拆到底全都是这几条动作的排列组合。
有了 JMP(跳转),程序就能重复("没算完就跳回去再算一遍")和分情况("结果是 0 就走这条路,不是 0 走那条")。加上加法和搬运,理论上就能写出任何程序——这是 1936 年图灵证明过的:只要会"读、写、按条件跳",就能模拟一切计算。
2. 一条指令长什么样:动词 + 宾语
CPU 的每条指令都是一串固定的 0/1,结构永远是两段:
10111000 00000101
└──操作码──┘└─操作数─┘
"放入" "数字 5"
- 操作码(opcode):干什么(动词)——这里是"把后面的数放入寄存器"
- 操作数(operand):对谁做(宾语)——这里是数字 5
菜谱:"取鸡蛋 2 个 → 打散 → 热油 → 下锅 → 翻炒 3 分钟"。 程序:"取 5 放入 A → 取 3 放入 B → A 加 B → 结果存入内存 → 显示"。 写程序 = 写菜谱。区别只在于:这位厨师一丝不苟到病态,菜谱少写一个字它就停摆,写错一个字它就照做不误。
寄存器是什么?就是 CPU 里离加法器最近的几个小抽屉(每个能存一个数)。算之前先把数从内存搬进抽屉,算完再搬回去——因为加法器(03 篇)只认抽屉里的数。
3. 同一件事,四种写法
任务:算 5 + 3,把结果存起来。看看它在不同层次的模样:
| 层次 | 写法 | 谁来看 |
|---|---|---|
| 机器语言 | 10111000 00000101 00000110 00000011… |
只给 CPU 看 |
| 汇编语言 | MOV AL, 5ADD AL, 3MOV 结果, AL |
给爱较真的人看 |
| C 语言 | int result = 5 + 3; |
给程序员看 |
| Python | result = 5 + 3 |
给人和程序员看 |
四种写法干的是同一件事,只是抽象层级不同:
- 机器语言(1940s):CPU 唯一的原生语言。人写不动、读不动,还全是 0/1。
- 汇编语言(1950s 前后):给 0/1 起了助记名——
MOV就是"搬运"。一行汇编 ≈ 一条机器指令,本质没变,只是把 0/1 换成了单词。汇编依然和 CPU 型号绑定:给 Intel 写的汇编,换到手机芯片上完全不通。 - 高级语言(1957 年起):终于说人话了。
5 + 3一行搞定,不关心哪个抽屉、哪条指令。而且和机器型号无关——同一份 Python 代码,Intel 电脑和手机都能跑。
不是"更好写",而是把人和机器解耦了。程序员从此描述"要做什么",至于翻译成哪几条机器指令——那是编译器的事。这就是下一节的主角。
4. 翻译官:编译器与解释器
电脑不认识 result = 5 + 3,中间必须有个翻译官把高级语言翻成机器码。翻译有两种方式:
| 编译(提前翻译) | 解释(现场翻译) | |
|---|---|---|
| 类比 | 出版前整本书译好 | 同声传译,说一句译一句 |
| 代表语言 | C、C++、Rust | Python、JavaScript |
| 流程 | 源代码 → 编译器 → 可执行文件(.exe) | 源代码 → 解释器边读边执行 |
| 优点 | 译好了直接跑,速度快 | 拿来就跑,改起来快 |
| 缺点 | 每次改完要重新编译 | 每次跑都要重新译,慢一些 |
- 你双击的每个
.exe文件,本质就是一份早已翻译成机器码的指令清单,存硬盘上等着 CPU 执行。 - 你在 Python 里跑的脚本,则是解释器当场翻译执行的。
Java、C# 这类语言走中间路线:先编译成"半成品"(字节码),运行时再由一个"虚拟机"现场翻给 CPU。现代解释型语言(Python、JavaScript)其实也带各种即时编译优化。小白阶段记住"要么提前译好,要么现场翻译"这个骨架就够了。
5. 存储程序:最漂亮的一步棋
现在把 03、04、05 串起来,看计算机体系结构里最核心的一条设计——存储程序(Stored Program)思想,1945 年由冯·诺依曼团队确立:
程序本身也是 0 和 1,和数据一样,存在同一块内存里。
这一句话的分量极重:
- CPU 干活就是循环三件事:从内存取一条指令 → 译出要干什么 → 执行它,然后取下一条。每秒循环几十亿次(这就是"3GHz"的意思——时钟每秒滴答 30 亿次)。
- 因为程序是数据,程序就可以被另一个程序修改、生成、加载——你安装一个 App 不用重新造一台电脑,操作系统把新指令清单写进内存就行。同一台硬件,装不同软件就是不同的机器——今天电脑万能的根源全在这。
- 双击程序 → 操作系统把机器码从硬盘复制进内存
- CPU 取指:
把 5 放入寄存器 A - CPU 取指:
把 3 放入寄存器 B - CPU 取指:
A 加 B→ 加法器(03 篇造的那个)通电,输出 8 - CPU 取指:
结果送回内存 - CPU 取指:
调用显示→ 屏幕出现 8 全程没有人手工干预——CPU 只是在忠实地念你的菜谱。
6. 程序员每天到底在干什么
知道了"编程 = 写菜谱",就能理解这个职业的真实日常:
- 拆问题:把"做个记账软件"拆成几百个 CPU 动作能表达的小步骤(这是主要工作,占 80%)
- 写代码:把步骤用编程语言精确地写下来
- 调试(Debug):菜谱写错了一步,厨师照做不误——结果菜是苦的。程序员的工作就是找到自己和机器之间理解不一致的地方。程序从不"出错",它只会精确执行你写错的指令
- 改需求:客人明天想加辣——回到第 1 步
1947 年,哈佛 Mark II 计算机(继电器机器,04 篇讲过)出了故障,工程师格雷丝·霍珀在继电器里揪出一只真·飞蛾,贴进了工作日志,写道"发现第一个 actual bug(虫子)"。从此"程序毛病"都叫 bug,"抓毛病"叫 debugging。你已经在 04 篇见过这只蛾子了。
7. 那我需要学一门编程语言吗?
先给结论:要不要背语法另说,"把问题拆成明确步骤"的能力,现在就值得练。
用大白话验证一下自己:能不能把"泡一杯茶"写成一份没有任何歧义的菜谱?试着写写看,你会发现:
- "烧水"——烧到多少度?用什么烧?水没了怎么办?
- "等水开"——等多久?怎么知道开了?
- 每一步都要有明确的判断依据和备用方案
这份"强迫症式拆解"就是编程思维(也叫算法思维)。语法是方言,随时能查;拆解是普通话,得练。
现在 AI 能帮你写代码了,那编程还有用吗?——AI 是更高一级的翻译官:你用自然语言说需求,它翻译成代码。但你依然得会拆解问题、看懂结果、发现"翻译错了"。上一层的翻译工具变了,"把意图拆精确"这个底层能力没变,反而更重要了。
8. 常见误区
需要小学算术和逻辑清晰,不需要高数。程序员最常用的数学是加减乘除和比较大小。
[!warning] 误区二:编程语言就是英语,会英语就行
是形式语言,和英语的区别是:它不允许任何歧义,一个标点错了都不行。英语里说错话对方会脑补,机器不会——它只会照错执行。
[!warning] 误区三:电脑"理解"了我的程序
电脑从不理解任何东西。它只是高速、忠实地执行指令。所谓"智能软件",也是人把智能所需的步骤一条条写出来的。
[!warning] 误区四:改个后缀名就能换语言
.py 改成 .exe 不会变成可执行文件——格式看内容不看名字(这个坑到"文件与扩展名"篇细讲)。
[!warning] 误区五:代码越短越厉害
代码首先是写给人看的(第二才是给机器执行的)。清晰但长一点的代码,远好过玄妙但短一行的代码。
9. 可折叠自测题
约百来条(搬运、加减、逻辑、比较、跳转)。
不能理解,只会忠实地逐条执行。"理解"从来都发生在写程序的人脑子里。
[!question]- Q2. 一条机器指令由哪两部分组成?
操作码(干什么,动词)+ 操作数(对谁做,宾语)。
例:10111000 00000101 = "把 5 放入寄存器"。
[!question]- Q3. 编译和解释的区别是什么?各举一门语言。
编译 = 出版前整本译好(C/C++ → 生成 .exe,跑得快);
解释 = 同声传译,边读边执行(Python/JavaScript,改起来快,跑得慢些)。
[!question]- Q4. "存储程序"思想说的是什么?它为什么让电脑变成万能机器?
程序本身也是 0/1 数据,和数据存在同一块内存里,CPU 取指→译码→执行循环处理。
因为程序是数据,就能被随时加载、更换——同一台硬件,装不同软件就是不同机器。
[!question]- Q5. 程序出 bug 了,是电脑"出错"了吗?
不是。电脑精确执行了你写错的指令。bug 的本质是"你写的"和"你想要的"不一致——所以调试是找回这个偏差,而不是修机器。
10. 下一步学什么
- [x] 接收指令的装置(CPU) —— 已写成 第 06 篇:指令下给谁、谁来执行
- [ ] 十六进制与负数表示 —— 颜色代码
#FF8800、内存地址,原码/反码/补码为什么会演变出补码(03 已讲用法,此篇讲由来) - [ ] 数据压缩原理 —— 哈夫曼、RLE,ZIP 到底做了什么
- [ ] 计算机五大部件 —— CPU 之外,一台完整的电脑还需要什么
- [ ] 操作系统做什么 —— 几十个程序怎么排队用同一个 CPU
- [ ] 写第一行真代码 —— 想动手的话,Python 是最好的第一门语言,说一声我带你装环境
相关笔记
- 04 实现电路计算之路 ← 上一篇:造出会算的机器的人
- 03 用电表示计算-计算机加法运算 ← CPU 执行指令时,真正干活的电路
- 02 信息的数字化表示 ← 一切信息怎么变成 0/1
- 01 二进制原理入门 ← 0 和 1 本身
- 📇 计算机基础 · 索引
- 🏠 返回主页