<!-- title: 代码如何被读懂：编译器神课深读 -->
<!-- domain: programming -->

# 代码如何被读懂：一条数百万播放的编译器神课深读

## 概述

AI 都能替你写代码的今天，很多人敲了三五年键盘，却从没想过一个最基本的问题：**你写下的那串字符，计算机是怎么“看懂”的？** 编程语言圈有一条长青爆款视频——Frame of Essence 频道的《How do computers read code?》（计算机如何读代码），它用不到五分钟的动画，把编译器与解释器的工作原理讲成了一个人人能懂的“翻译”故事。这条视频常年霸占“编译器入门”相关搜索的前列，累计播放达数百万次，是无数计算机专业学生和自学者接触“语言实现”这个领域的第一课。本文按照视频的叙事顺序逐段拆解，把它省略的工程细节、背后的真实历史与理论全部补齐——没看过视频，你能看懂；看过视频，你能多懂一层。

## 视频基本信息

| 项目 | 内容 |
|------|------|
| 视频标题 | How do computers read code?（计算机如何读代码？） |
| 频道 | Frame of Essence |
| 主题分类 | 编程语言实现 / 编译原理科普 |
| 时长 | 约 4 分钟动画短片 |
| 热度 | 播放达数百万次，是该频道播放量最高的视频之一，长期被各大学课程与论坛引用为编译入门首选 |

※ 说明：本篇撰写环境无法联网实时核对页面，以上播放量与时长为据频道页面的历史数据，请以 YouTube 当前页面为准。

## 核心主张拆解：视频到底讲了什么

这条视频的全部论证，建立在一个隐喻上：**编程语言是人与机器之间的“外语”，读懂代码的本质是翻译。** 按叙事顺序，它讲了五件事。

### 要点一：机器只认识自己的母语

视频开场先立起一个反差：人类写的代码——`if`、`while`、变量、函数——在 CPU 眼里毫无意义。CPU 是一台“只听得懂母语”的机器，它的母语是机器码：一串串固定长度的二进制指令，每条指令对应一个极原始的操作，比如“把这个寄存器里的数加一”“把这个地址的数据搬过去”。人类不可能直接用这种语言思考。程序员的创造力靠的是高级语言，而高级语言和机器码之间的鸿沟，必须有人来填——这个“翻译官”，就是视频的主角。

### 要点二：翻译有两种哲学——编译与解释

这是全片最经典的段落。视频用“翻译一本书”作比，给出了两种截然不同的策略：

- **编译（compile）**：像把一整本外文书**先完整翻译成中文再出版**。翻译官（编译器）拿到全部原文，通读、理解、转换，最后产出一本全新的目标语言版本——对应到计算机，就是一次性把源代码整体翻译成机器码，生成可执行文件。之后每次运行，直接跑机器码，翻译官不再在场。
- **解释（interpret）**：像**同声传译**。译者拿着原文，一边听（读）一边当场翻给你听，原书自始至终保持外文原样——对应到计算机，就是解释器逐句读取源代码，读一句、执行一句，不产出独立的可执行文件。

视频明确指出两者的代价结构不同：编译慢在运行前，快在运行时；解释快在启动，慢在执行——因为每次运行都要重新翻译一遍。这个“时间花在哪里”的框架，是理解一切语言性能差异的起点。

### 要点三：翻译官怎么读代码——先拆词

接下来是全片技术含量最高的部分：编译器（或解释器）读代码的第一步，**词法分析（lexical analysis）**。

编译器看到的源代码，最初只是一长串字符。它做的第一件事是“断句分词”：把 `int count = 10;` 这样的字符流，切成一个个有意义的**记号（token）**——`int` 是关键字，`count` 是标识符，`=` 是运算符，`10` 是数字字面量，`;` 是分隔符。干这活的组件叫**词法分析器（lexer / tokenizer）**。

视频用了一个精妙的类比：这就像你读一门没学过的外语时，先得知道哪里是一个词的结尾、哪些字符组合构成一个“单词”。空格和换行帮了人类的忙，但对机器来说，它需要明确的规则来判定“这串字符是一个整体”。

### 要点四：再组句——把单词拼成一棵树

第二步，**语法分析（parsing）**。有了单词还不够——“ count int = 10”打乱顺序就毫无意义。语言之所以是语言，在于单词必须按**语法规则**组合。语法分析器（parser）拿着一套语法规则，把记号流组装成**抽象语法树（Abstract Syntax Tree，AST）**。

比如 `2 + 3 × 4`，解析出来的不是一串平铺的记号，而是一棵树：乘法是根节点，`3` 和 `4` 是它的孩子，而整个乘法节点又是加法节点的一个孩子——这棵树的结构，天然编码了“先乘后加”的优先级。视频在这里点破了全片最深的一层认知：**代码在机器内部的存在形式不是文本，而是一棵树。** 你写的是一行行字符串，机器“想”的却是一棵层级结构。

### 要点五：最后一步——遍历这棵树，翻成目标语言

树建好了，翻译就是水到渠成的事：

- **编译器**遍历这棵语法树，把每个节点翻译成机器指令：加法节点翻译成一条 `ADD` 指令，赋值节点翻译成“取数—写入内存”的指令序列。最终输出一份完整的机器码文件。
- **解释器**同样要经过拆词和建树，但它不做“翻译成机器码”这一步，而是**直接照着树执行**——看到加法节点就算加法，看到赋值节点就改内存。

视频的收尾升华了主题：编译器和解释器本质上是同一件事的两种完成度——“把一种语言翻译成另一种语言”。而最有意思的地方在于：**翻译官自己也是程序，也是用某种语言写的**。这个自指结构，为整个领域埋下了最深的一颗种子。

## 知识与理论溯源：视频背后的真实历史与理论

这条视频是科普浓缩，它背后的每一环都有扎实的工程史与理论支撑。逐环补齐：

### 第一个编译器：Grace Hopper 与 A-0

“编译器”这个概念的工程起点，通常追溯到**格蕾丝·霍珀（Grace Hopper）**。1952 年，她在 UNIVAC 上实现了 **A-0 编译器**——能把人们用助记方式写下的例程描述，翻译成机器可执行的代码。霍珀后来的 FLOW-MATIC 语言（1957）直接催生了商用到今天的 **COBOL**。她坚持“程序应该用接近英语的方式写、让机器去适应人而不是人适应机器”，这个理念就是“高级语言”的灵魂。今天我们说“编译”，词源上就是霍珀一代人把"compile"（汇编、编纂）从出版业借来用的结果。

### FORTRAN 与第一代真正的编译器

1957 年，**约翰·巴科斯（John Backus）**带领 IBM 团队交付了 **FORTRAN 编译器**——第一个能生成高效机器码的高级语言编译器。它证明了一件事：机器生成的代码可以比手写汇编更优化，从此“用高级语言写程序”在经济上成立了。巴科斯随后在 ALGOL 58/60 报告中与**彼得·瑙尔（Peter Naur）**共同确立了 **BNF 范式（Backus–Naur Form）**——一种用“产生式规则”精确描述语法的方式。视频里“语法规则决定单词怎么组句”，其严格的数学形态就是 BNF。

### 乔姆斯基层级：为什么“拆词”和“组句”要用两套工具

视频先拆词、再组句的两步走，并非工程直觉，而是语言学理论的直接映射。1956 年，语言学家**诺姆·乔姆斯基（Noam Chomsky）**发表《Three Models for the Description of Language》，提出著名的**乔姆斯基层级**，把语言按复杂度分成四层。对编译器设计者最重要的区分是：

- **正则语言（3 型）**：结构简单，用有限状态机就能识别——词法分析恰好落在这层。从代码里识别“这是一个标识符/这是一个数字”，正则表达式就够了。
- **上下文无关语言（2 型）**：能表达嵌套结构（括号套括号、语句套语句）——程序语言的语法大体落在这层，需要下推自动机、也就是视频里的“按语法规则建树”来处理。

一句话：**词法用正则，语法用文法**——两步走是数学上的必然，不是风格选择。（顺带一提：解析领域后来发展出 LL、LR 等算法族，其中 LR(k) 由高德纳的学生时代论文奠基，即 Donald Knuth 于 1965 年发表的 LR 理论；工程上更常用手写的“递归下降解析器”，现代大量语言的编译器前端都采用它。）

### 解释一侧的谱系：Lisp 与 eval/apply

解释执行的谱系从**约翰·麦卡锡（John McCarthy）**1958 年的 **Lisp** 开始。Lisp 的代码和数据同构（都是 S 表达式），麦卡锡用半页纸的 `eval` 函数就定义了整个语言——这是“解释器”概念的理论起点。视频里“解释器照着语法树直接执行”的描述，与 Lisp 的 `eval/apply` 循环一脉相承。

### 现代工程：没有纯粹的编译器，也没有纯粹的解释器

视频的二分法是教学简化。真实世界的主流引擎几乎全是**混合模式**：

- **V8**（Chrome 与 Node.js 的 JavaScript 引擎）先用**解释器 Ignition** 快速启动，识别出高频“热点代码”后，再交给**优化编译器 TurboFan** 编译成机器码——同一段代码，冷时解释、热时编译。
- **Java** 是“先编译成字节码、再由 JVM 解释 + JIT”的三段式；**Python** 会把源码编译成字节码并缓存为 `.pyc`，下次免解析；**LuaJIT、PyPy** 都是解释器加即时编译的组合。
- 后端统一的代表是 **LLVM**：克里斯·拉特纳（Chris Lattner）2000 年代初在伊利诺伊大学创立的项目，把“前端（各种语言→中间表示）”与“后端（中间表示→各种 CPU 指令）”解耦。今天 Swift、Rust 的编译器都构建在 LLVM 之上——一种新语言只要写个前端，就能白得全平台代码生成与几十年积累的优化。

还有一个可爱的自指结构值得单独说：**自举（bootstrapping）**——用 C 语言写 C 的编译器，用 Rust 写 Rust 的编译器。这带来一个著名的深水区问题：肯·汤普森（Ken Thompson，Unix 之父）1984 年图灵奖演讲《Reflections on Trusting Trust》指出：如果编译器本身被植入后门，它可以悄悄把后门“传染”给每一个它编译出来的程序，甚至在编译自己的源码时也看不出来。读代码这件事的信任链，最终要落在“你相信哪个编译器”上——这是视频没讲、但每个系统程序员都该知道的一层。

## 批判性审查：哪些成立，哪些被简化

**证据充分的部分**：词法分析→语法分析→AST→生成/执行的流水线框架，是教科书级的共识（龙书《Compilers: Principles, Techniques, and Tools》——Aho、Lam、Sethi、Ullman 合著——前几章就是这个结构），没有任何争议。AST 作为“代码的机器内部形态”也完全准确，今天的 ESLint、Prettier、Tree-sitter 全部工作在 AST 上。

**被简化的部分**需要三点提醒：

1. **“编译 vs 解释”不是语言的属性，而是实现的选择。** 视频为叙事清晰，容易让人以为“C 是编译型语言、Python 是解释型语言”是语言的固有标签。严格说：存在 C 的解释器和 Python 的编译器（CPython 就先把你的代码编译成字节码）。“编译型/解释型”是口头禅，不是技术分类——同一种语言完全可以被编译，也可以被解释。
2. **省略了语义分析与优化。** 语法正确的代码未必有意义：`int x = "hello";` 能建出一棵树，但类型对不上。真实编译器在 AST 之后还有**语义分析（类型检查等）**、**中间表示**和动辄几十上百个**优化 pass**。视频跳过了从“正确的树”到“高效的机器码”之间最难、也最值钱的部分——这没问题，但读者要知道那才是编译器工程的大头。
3. **“解释器运行慢”的现代注脚。** 视频的年代背景让解释显得天然吃亏，但现代解释器普遍先编译成字节码再解释（免去重复解析），配合 JIT 后热点代码速度可逼近静态编译。启动速度与峰值速度的权衡，早已不是简单的谁快谁慢。

另有一个真实的工程争议可以补充：语法本身也是复杂性来源。C++ 的模板语法让“拆词建树”变得病态困难（尖括号既可能是比较运算符又可能是模板括号，需要语义信息才能定论），以至于 C++ 编译器前端常年是最难写的软件之一——这是“上下文无关”在实践中被打破的著名案例。

## 认知提升与实用启示

**第一，把编译器错误信息当成你的第一次 code review。** 理解了流水线，你就知道错误信息的层级：`unexpected character` 是词法层在说话，`expected ';'` 是语法层在说话，`type mismatch` 是语义层在说话。读懂报错属于哪一层，定位速度立刻翻倍——报错发生在哪一步，问题就大致出在哪一段。

**第二，学会用“代码即树”的视角做自动化。** 既然代码在机器里是 AST，那么批量改代码的正道就是操作 AST 而不是正则替换：ESLint 规则、Prettier 格式化、各大厂的 codemod 迁移工具，本质都是“遍历语法树、改节点、再打印回去”。下次想对代码库做大规模修改时，先想想“这能不能在语法树上做”，而不是硬写正则。

**第三，动手写一个 200 行的迷你解释器。** 这是消化这条视频最狠的方式。Bob Nystrom 的免费在线书《Crafting Interpreters》会带你用不到千行代码写出一个能跑的 LISP 方言与脚本语言； Thorsten Ball 的《Writing an Interpreter in Go》是另一条常被推荐的路径。写完拆词和建树，你会对“代码是什么”产生永久性的认知升级——从此你看任何语言，看到的都不再是字符，而是树。

## 视频论证结构图

```mermaid
flowchart TD
    A[人写高级语言] --> B[机器只懂机器码]
    B --> C[需要翻译官]
    C --> D{翻译哲学二选一}
    D --> E[编译器<br/>先整本翻译]
    D --> F[解释器<br/>边读边执行]
    E --> G[第一步 词法分析<br/>字符切成记号]
    F --> G
    G --> H[第二步 语法分析<br/>记号拼成语法树]
    H --> I[编译路线<br/>遍历树生成机器码]
    H --> J[解释路线<br/>照着树直接执行]
    I --> K[产物可反复运行]
    J --> L[每次运行重复翻译]
    K --> M[翻译官也是程序<br/>自举与信任链]
    L --> M
```

## 结语

这条视频最了不起的地方，是用四分钟把“编译原理”从一门劝退课变成了一次顿悟：**读代码和读文章，遵循同样的语言学规律；而读懂代码的那个翻译官，本身也是一段被翻译出来的代码。** 在 AI 代写代码的时代，“代码如何被读懂”这个问题有了新的分量——机器读得越来越懂，人反而更需要理解读代码的机制本身。看完视频再回头看本文补上的那些名字：霍珀、巴科斯、乔姆斯基、麦卡锡、拉特纳——你会发现，这四分钟动画里，压缩着七十年语言实现史的全部主干。

## 延伸阅读

1. [Crafting Interpreters（免费在线全书）](https://craftinginterpreters.com) — Bob Nystrom 著，手把手带你写出两个完整解释器，是本文主题公认的实践首选。
2. [Compiler - Wikipedia](https://en.wikipedia.org/wiki/Compiler) — 编译器结构、历史与各阶段详解的权威综述条目。

## 参考来源

1. [Frame of Essence - YouTube 频道页](https://www.youtube.com/@FrameOfEssence) — 本文解读视频《How do computers read code?》的出品频道（本次环境无法联网，请以站内搜索 "How do computers read code" 获取最新播放数据）。
2. [Crafting Interpreters](https://craftinginterpreters.com) — 延伸阅读推荐的免费在线书籍官网。
3. [Compiler - Wikipedia](https://en.wikipedia.org/wiki/Compiler) — 编译器历史（Grace Hopper、FORTRAN、BNF 等）与流水线结构的交叉验证来源。
4. [Chomsky hierarchy - Wikipedia](https://en.wikipedia.org/wiki/Chomsky_hierarchy) — 乔姆斯基层级与正则/上下文无关语言的理论背景来源。

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
