feat(docs): add interactive demos and complete content for development tools
- Add Vue components for interactive demos (SSH auth, regex, env vars, ports) - Complete markdown content for SSH, regex, environment variables, and ports - Remove placeholder "待实现" sections and replace with detailed guides - Add visual explanations for key concepts like ports and localhost - Include practical examples and troubleshooting tips - Add component for showing evolution from transistors to CPU - Improve documentation structure and navigation - Add security best practices for API keys and environment variables
This commit is contained in:
@@ -1,395 +1,132 @@
|
||||
# 编程语言图谱
|
||||
|
||||
::: tip 🎯 核心问题
|
||||
**为什么有这么多编程语言?它们之间有什么关系?** 从机器语言到现代高级语言,每种语言都有其设计哲学和适用场景。本章带你理解编程语言的演化历程和核心概念。
|
||||
:::
|
||||
> 💡 **学习指南**:为什么有这么多编程语言?该学哪个?本章带你从"语言演化"到"编程范式"到"如何选择",建立对编程语言全景的理解。**结论先行:没有最好的语言,只有最适合场景的语言。**
|
||||
|
||||
---
|
||||
|
||||
## 0. 想象你要和外国人交流:
|
||||
## 0. 人类如何和计算机"说话"?
|
||||
|
||||
- **直接用肢体语言**:最原始,但效率极低(机器语言)
|
||||
- **学习对方的语言**:需要翻译,但表达丰富(高级语言)
|
||||
- **使用世界语**:设计完美,但没人用(某些学术语言)
|
||||
- **使用翻译软件**:自动转换,但可能不准确(编译器/解释器)
|
||||
想象你要和一个只懂二进制的机器人沟通:
|
||||
|
||||
**编程语言就是人类与计算机沟通的桥梁**,不同的语言有不同的设计哲学。
|
||||
- **直接打 0 和 1** — 最原始,效率极低,一个 0 写成 1 就全错了(机器语言)
|
||||
- **用助记符代替** — `MOV AX, 1` 比 `10110000 00000001` 好认多了(汇编语言)
|
||||
- **用接近自然语言** — `int sum = 1 + 2;` 人类可以直接读懂(高级语言)
|
||||
|
||||
<LanguageMapDemo />
|
||||
**编程语言就是人类与计算机沟通的桥梁**,70 多年来一直在朝着"更接近人类思维"的方向进化。
|
||||
|
||||
---
|
||||
|
||||
## 1. 编程语言的演化
|
||||
|
||||
### 1.1 第一代:机器语言(1940s)
|
||||
👇 动手点点看:探索编程语言从 1940 年代到今天的演化历程
|
||||
|
||||
::: tip 💡 机器语言是什么?
|
||||
直接用 0 和 1 编写程序,计算机可以直接执行。
|
||||
<LanguageMapDemo />
|
||||
|
||||
**示例**:让计算机计算 1 + 2
|
||||
```
|
||||
10110000 00000001 ; 将 1 放入寄存器
|
||||
10110001 00000010 ; 将 2 放入另一个寄存器
|
||||
10100010 ; 执行加法
|
||||
```
|
||||
|
||||
**问题**:
|
||||
- 人类难以理解和记忆
|
||||
- 容易出错,一个 0 写成 1 就全错了
|
||||
- 不同 CPU 有不同的机器语言
|
||||
::: tip 💡 一句话总结
|
||||
编程语言的演化趋势:**越来越接近人类思维,越来越安全,越来越高效**。从手写 0/1,到汇编助记符,到 C 的结构化编程,到 Java 的面向对象,再到 Rust 的内存安全——每一代语言都在解决上一代的痛点。
|
||||
:::
|
||||
|
||||
### 1.2 第二代:汇编语言(1950s)
|
||||
|
||||
用**助记符**代替 0 和 1:
|
||||
|
||||
```asm
|
||||
MOV AX, 1 ; 将 1 放入 AX 寄存器
|
||||
MOV BX, 2 ; 将 2 放入 BX 寄存器
|
||||
ADD AX, BX ; 将 BX 加到 AX
|
||||
```
|
||||
|
||||
::: tip 💡 汇编语言 vs 机器语言
|
||||
| 特性 | 机器语言 | 汇编语言 |
|
||||
|------|---------|---------|
|
||||
| **可读性** | 极差 | 较好 |
|
||||
| **执行效率** | 最高 | 最高(汇编器直接转换) |
|
||||
| **移植性** | 无 | 无(依赖 CPU 架构) |
|
||||
| **使用场景** | 几乎不用 | 嵌入式、操作系统内核 |
|
||||
:::
|
||||
|
||||
### 1.3 第三代:高级语言(1950s - 至今)
|
||||
|
||||
**用接近自然语言的方式编程**:
|
||||
|
||||
```c
|
||||
int sum = 1 + 2; // C 语言
|
||||
```
|
||||
|
||||
**里程碑语言**:
|
||||
|
||||
| 年代 | 语言 | 意义 |
|
||||
|------|------|------|
|
||||
| **1957** | Fortran | 第一个高级语言,科学计算 |
|
||||
| **1958** | Lisp | 函数式编程鼻祖 |
|
||||
| **1959** | COBOL | 商业数据处理 |
|
||||
| **1972** | C | 系统编程,影响深远 |
|
||||
| **1983** | C++ | 面向对象 + C 的效率 |
|
||||
| **1991** | Python | 简洁优雅,AI 时代主角 |
|
||||
| **1995** | Java | 跨平台,企业应用 |
|
||||
| **1995** | JavaScript | Web 开发,无处不在 |
|
||||
| **2009** | Go | 并发友好,云原生 |
|
||||
| **2010** | Rust | 内存安全,系统编程新选择 |
|
||||
|
||||
### 1.4 第四代:领域特定语言(DSL)
|
||||
|
||||
为特定领域设计的语言:
|
||||
|
||||
| 语言 | 领域 | 示例 |
|
||||
|------|------|------|
|
||||
| **SQL** | 数据库查询 | `SELECT * FROM users` |
|
||||
| **HTML** | 网页结构 | `<div>Hello</div>` |
|
||||
| **CSS** | 样式定义 | `color: red;` |
|
||||
| **Regex** | 文本匹配 | `\d{3}-\d{4}` |
|
||||
| **MATLAB** | 数学计算 | `A = [1 2; 3 4]` |
|
||||
|
||||
---
|
||||
|
||||
## 2. 编程范式:思考问题的方式
|
||||
|
||||
::: tip 💡 什么是编程范式?
|
||||
编程范式是**编程的思维方式**,决定了你如何组织代码和解决问题。
|
||||
编程范式不是语言特性,而是**思维方式**——就像写作有诗歌、小说、论文不同的文体。
|
||||
|
||||
就像写作有不同的文体(诗歌、小说、论文),编程也有不同的"文体"。
|
||||
:::
|
||||
|
||||
### 2.1 命令式编程(Imperative)
|
||||
|
||||
**核心思想**:告诉计算机"怎么做"
|
||||
### 2.1 命令式 — "一步步告诉计算机怎么做"
|
||||
|
||||
```c
|
||||
// 计算数组总和
|
||||
int sum = 0;
|
||||
for (int i = 0; i < n; i++) {
|
||||
sum += arr[i];
|
||||
}
|
||||
```
|
||||
|
||||
**特点**:
|
||||
- 关注**过程**和**步骤**
|
||||
- 通过**语句**改变程序状态
|
||||
- 最接近计算机实际执行方式
|
||||
|
||||
**代表语言**:C, Fortran, BASIC
|
||||
|
||||
### 2.2 面向对象编程(OOP)
|
||||
|
||||
**核心思想**:把数据和操作封装在"对象"中
|
||||
### 2.2 面向对象 — "把数据和行为封装成对象"
|
||||
|
||||
```python
|
||||
class Dog:
|
||||
def __init__(self, name):
|
||||
self.name = name
|
||||
|
||||
def bark(self):
|
||||
print(f"{self.name} says woof!")
|
||||
|
||||
dog = Dog("Buddy")
|
||||
dog.bark() # Buddy says woof!
|
||||
```
|
||||
|
||||
**四大特性**:
|
||||
|
||||
| 特性 | 含义 | 生活类比 |
|
||||
|------|------|---------|
|
||||
| **封装** | 隐藏内部细节 | 汽车方向盘,不需要知道引擎原理 |
|
||||
| **继承** | 子类继承父类 | 儿子继承父亲的基因 |
|
||||
| **多态** | 同一接口不同实现 | 不同动物发出不同叫声 |
|
||||
| **抽象** | 提取共同特征 | "动物"是对猫、狗的抽象 |
|
||||
|
||||
**代表语言**:Java, C++, Python, Ruby
|
||||
|
||||
### 2.3 函数式编程(Functional)
|
||||
|
||||
**核心思想**:把计算视为函数求值,避免状态变化
|
||||
### 2.3 函数式 — "用纯函数组合,不修改状态"
|
||||
|
||||
```haskell
|
||||
-- 计算数组总和
|
||||
sum arr = foldl (+) 0 arr
|
||||
|
||||
-- 或者更简洁
|
||||
sum = foldl (+) 0
|
||||
-- 相同输入永远产生相同输出
|
||||
```
|
||||
|
||||
**核心原则**:
|
||||
|
||||
| 原则 | 含义 | 好处 |
|
||||
|------|------|------|
|
||||
| **纯函数** | 相同输入永远产生相同输出 | 易测试、易推理 |
|
||||
| **不可变数据** | 数据一旦创建就不变 | 无副作用、线程安全 |
|
||||
| **高阶函数** | 函数可以作为参数和返回值 | 代码复用、灵活组合 |
|
||||
| **无副作用** | 函数不修改外部状态 | 可预测、易调试 |
|
||||
|
||||
**代表语言**:Haskell, Lisp, Erlang, F#
|
||||
|
||||
### 2.4 声明式编程(Declarative)
|
||||
|
||||
**核心思想**:告诉计算机"做什么",而不是"怎么做"
|
||||
### 2.4 声明式 — "只说做什么,不管怎么做"
|
||||
|
||||
```sql
|
||||
-- 查询所有活跃用户
|
||||
SELECT name, email
|
||||
FROM users
|
||||
WHERE active = true
|
||||
ORDER BY created_at DESC
|
||||
SELECT name FROM users WHERE active = true
|
||||
-- 数据库自己决定怎么查最快
|
||||
```
|
||||
|
||||
**对比命令式**:
|
||||
|
||||
| 命令式 | 声明式 |
|
||||
|--------|--------|
|
||||
| "从第一行开始遍历..." | "给我所有活跃用户" |
|
||||
| "检查每个用户是否活跃..." | "按创建时间排序" |
|
||||
| "如果活跃就加入结果..." | 数据库自己决定怎么执行 |
|
||||
| "最后排序返回..." | |
|
||||
|
||||
**代表语言**:SQL, Prolog, HTML
|
||||
::: tip 💡 实际开发中
|
||||
现代语言大多是**多范式**的。Python 既支持面向对象,也支持函数式;JavaScript 也一样。不用纠结"哪个范式最好",而是根据问题选择最合适的方式。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 3. 类型系统:数据的分类规则
|
||||
## 3. 类型系统:数据的交通规则
|
||||
|
||||
::: tip 💡 什么是类型系统?
|
||||
类型系统是编程语言的**交通规则**,规定数据如何分类和操作。
|
||||
| | 强类型 | 弱类型 |
|
||||
|---|---|---|
|
||||
| **静态** | Java, Rust, TypeScript — 最安全 | C, C++ — 高效但要小心 |
|
||||
| **动态** | Python, Ruby — 灵活且安全 | JavaScript, PHP — 灵活但易出错 |
|
||||
|
||||
就像现实世界:
|
||||
- **整数** = 整数类型(1, 2, 3...)
|
||||
- **文字** = 字符串类型("hello")
|
||||
- **是/否** = 布尔类型(true/false)
|
||||
:::
|
||||
**关键问题**:`"1" + 1` 等于什么?
|
||||
- **JavaScript(弱类型)**:`"11"` — 悄悄帮你转了
|
||||
- **Python(强类型)**:`TypeError` — 让你自己想清楚
|
||||
|
||||
### 3.1 静态类型 vs 动态类型
|
||||
|
||||
| 特性 | 静态类型 | 动态类型 |
|
||||
|------|---------|---------|
|
||||
| **类型检查时机** | 编译时 | 运行时 |
|
||||
| **代码示例** | `int x = 1;` | `x = 1` |
|
||||
| **错误发现** | 编译期就发现 | 运行时才发现 |
|
||||
| **灵活性** | 较低 | 较高 |
|
||||
| **性能** | 较高(编译优化) | 较低(运行时检查) |
|
||||
| **代表语言** | Java, C++, Rust, TypeScript | Python, JavaScript, Ruby |
|
||||
|
||||
**静态类型示例(Java)**:
|
||||
|
||||
```java
|
||||
String name = "Alice";
|
||||
name = 123; // 编译错误!类型不匹配
|
||||
```
|
||||
|
||||
**动态类型示例(Python)**:
|
||||
|
||||
```python
|
||||
name = "Alice"
|
||||
name = 123 # 没问题,运行时类型改变
|
||||
```
|
||||
|
||||
### 3.2 强类型 vs 弱类型
|
||||
|
||||
| 特性 | 强类型 | 弱类型 |
|
||||
|------|--------|--------|
|
||||
| **类型转换** | 不允许隐式转换 | 允许隐式转换 |
|
||||
| **类型安全** | 高 | 低 |
|
||||
| **代码示例** | `"1" + 1` 报错 | `"1" + 1 = "11"` |
|
||||
| **代表语言** | Python, Java, Rust | JavaScript, PHP, C |
|
||||
|
||||
**弱类型示例(JavaScript)**:
|
||||
|
||||
```javascript
|
||||
console.log("1" + 1) // "11" (字符串拼接)
|
||||
console.log("1" - 1) // 0 (自动转数字)
|
||||
console.log([] + []) // "" (空字符串)
|
||||
console.log([] + {}) // "[object Object]"
|
||||
```
|
||||
|
||||
**强类型示例(Python)**:
|
||||
|
||||
```python
|
||||
"1" + 1 # TypeError: can only concatenate str to str
|
||||
```
|
||||
|
||||
### 3.3 类型推断
|
||||
|
||||
现代语言可以**自动推断**变量类型:
|
||||
|
||||
```typescript
|
||||
// TypeScript
|
||||
let x = 1; // 推断为 number
|
||||
let y = "hello"; // 推断为 string
|
||||
|
||||
// Rust
|
||||
let x = 1; // 推断为 i32
|
||||
let y = "hello"; // 推断为 &str
|
||||
```
|
||||
想深入了解类型系统?→ [类型系统与编译原理入门](./type-systems-compilers)
|
||||
|
||||
---
|
||||
|
||||
## 4. 编译型 vs 解释型
|
||||
|
||||
::: tip 💡 程序如何运行?
|
||||
编程语言写的代码需要转换成机器能理解的指令,有两种主要方式:
|
||||
:::
|
||||
|
||||
### 4.1 编译型语言
|
||||
|
||||
**流程**:源代码 → 编译器 → 机器码 → 执行
|
||||
|
||||
```
|
||||
源代码 (main.c)
|
||||
↓
|
||||
编译器 (gcc)
|
||||
↓
|
||||
可执行文件 (main.exe)
|
||||
↓
|
||||
CPU 直接执行
|
||||
```
|
||||
|
||||
**特点**:
|
||||
|
||||
| 优点 | 缺点 |
|
||||
|------|------|
|
||||
| 执行速度快 | 编译时间长 |
|
||||
| 编译时发现错误 | 跨平台需要重新编译 |
|
||||
| 不需要运行时环境 | 调试较困难 |
|
||||
|
||||
**代表语言**:C, C++, Rust, Go
|
||||
|
||||
### 4.2 解释型语言
|
||||
|
||||
**流程**:源代码 → 解释器 → 逐行执行
|
||||
|
||||
```
|
||||
源代码 (main.py)
|
||||
↓
|
||||
解释器 (python)
|
||||
↓
|
||||
逐行解释执行
|
||||
```
|
||||
|
||||
**特点**:
|
||||
|
||||
| 优点 | 缺点 |
|
||||
|------|------|
|
||||
| 跨平台 | 执行速度慢 |
|
||||
| 开发调试快 | 运行时才能发现错误 |
|
||||
| 代码即运行 | 需要解释器环境 |
|
||||
|
||||
**代表语言**:Python, JavaScript, Ruby, PHP
|
||||
|
||||
### 4.3 混合型语言(JIT)
|
||||
|
||||
**即时编译(Just-In-Time)**:先解释执行,热点代码编译成机器码
|
||||
|
||||
```
|
||||
源代码
|
||||
↓
|
||||
字节码(中间代码)
|
||||
↓
|
||||
解释执行 + JIT 编译热点代码
|
||||
↓
|
||||
执行
|
||||
```
|
||||
|
||||
**代表语言**:Java, JavaScript (V8), Python (PyPy)
|
||||
| | 编译型 | 解释型 | JIT |
|
||||
|---|---|---|---|
|
||||
| **过程** | 先全部翻译,再执行 | 边读边执行 | 先解释,热点再编译 |
|
||||
| **速度** | 最快 | 较慢 | 中等 |
|
||||
| **调试** | 需编译等待 | 即时反馈 | 即时 + 优化 |
|
||||
| **代表** | C, Rust, Go | Python, Ruby | Java, JavaScript |
|
||||
|
||||
---
|
||||
|
||||
## 5. 如何选择编程语言?
|
||||
|
||||
::: tip 💡 没有最好的语言,只有最适合的语言
|
||||
选择语言要考虑:
|
||||
1. **问题领域**:Web 开发?系统编程?数据分析?
|
||||
2. **团队熟悉度**:团队擅长什么?
|
||||
3. **生态系统**:有没有现成的库?
|
||||
4. **性能需求**:需要多高的性能?
|
||||
5. **开发效率**:需要多快开发完成?
|
||||
:::
|
||||
### 按场景选择
|
||||
|
||||
### 5.1 按应用场景选择
|
||||
|
||||
| 场景 | 推荐语言 | 原因 |
|
||||
|------|---------|------|
|
||||
| **Web 前端** | JavaScript, TypeScript | 浏览器原生支持 |
|
||||
| **Web 后端** | Java, Go, Python, Node.js | 生态成熟,框架丰富 |
|
||||
| 场景 | 推荐语言 | 理由 |
|
||||
|---|---|---|
|
||||
| **Web 前端** | JavaScript, TypeScript | 浏览器只认 JS |
|
||||
| **Web 后端** | Go, Java, Python, Node.js | 生态成熟 |
|
||||
| **移动开发** | Swift (iOS), Kotlin (Android) | 官方推荐 |
|
||||
| **数据分析** | Python, R | 库丰富,社区活跃 |
|
||||
| **人工智能** | Python | TensorFlow, PyTorch |
|
||||
| **系统编程** | C, C++, Rust | 性能高,控制精细 |
|
||||
| **游戏开发** | C++, C#, Lua | 引擎支持 |
|
||||
| **嵌入式** | C, Rust | 资源受限环境 |
|
||||
| **云原生** | Go, Rust | 并发友好,部署简单 |
|
||||
| **AI / 数据** | Python | PyTorch、Pandas 全在 Python |
|
||||
| **系统编程** | C, Rust | 直接操控硬件 |
|
||||
| **云原生** | Go, Rust | Docker/K8s 都是 Go 写的 |
|
||||
|
||||
### 5.2 学习路线建议
|
||||
### 学习路线建议
|
||||
|
||||
**初学者**:
|
||||
1. Python(语法简单,应用广泛)
|
||||
2. JavaScript(Web 开发必备)
|
||||
3. 选择一门静态类型语言(Java 或 TypeScript)
|
||||
|
||||
**进阶**:
|
||||
1. 学习 C 理解底层
|
||||
2. 学习函数式编程思想(Haskell 或 F#)
|
||||
3. 学习 Rust 理解内存安全
|
||||
1. **Python** — 语法最简单,AI 时代入口
|
||||
2. **JavaScript** — Web 开发必备,前后端通吃
|
||||
3. **TypeScript** — 给 JS 加上类型系统,体验静态类型
|
||||
4. **Go 或 Rust** — 理解编译型语言和底层概念
|
||||
|
||||
---
|
||||
|
||||
## 6. 总结
|
||||
|
||||
::: tip 📚 核心要点
|
||||
1. **编程语言演化**:从机器语言到高级语言,越来越接近人类思维
|
||||
2. **编程范式**:命令式、面向对象、函数式、声明式,各有优劣
|
||||
3. **类型系统**:静态/动态、强/弱类型,影响代码安全和灵活性
|
||||
4. **运行方式**:编译型快但需编译,解释型慢但灵活
|
||||
5. **选择语言**:没有银弹,根据场景选择合适的工具
|
||||
1. **语言演化**:从机器语言到高级语言,越来越接近人类思维
|
||||
2. **编程范式**:命令式、面向对象、函数式、声明式,各有适用场景
|
||||
3. **类型系统**:静态/动态、强/弱,影响安全性和灵活性
|
||||
4. **运行方式**:编译型快,解释型灵活,JIT 兼顾
|
||||
5. **没有银弹**:根据场景选语言,而不是追求"最好的语言"
|
||||
:::
|
||||
|
||||
**下一步学习**:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# 从晶体管到 CPU
|
||||
|
||||
::: tip 🎯 核心问题
|
||||
::: tip 核心问题
|
||||
**计算机是怎么"思考"的?** 你可能知道 CPU 是电脑的"大脑",但这个大脑到底是怎么工作的?它怎么从一堆金属和塑料变成能执行程序、处理数据的智能设备?本章带你从最底层的晶体管开始,一步步理解 CPU 的构造原理。
|
||||
:::
|
||||
|
||||
@@ -8,22 +8,20 @@
|
||||
|
||||
## 0. 全景图:从沙子到智能
|
||||
|
||||
<TransistorDemo />
|
||||
|
||||
现代计算机的"思考"能力,归根结底来自于一个简单的东西:**开关**。
|
||||
|
||||
想象你有一个开关,可以控制灯的亮灭。现在,如果你有几十亿个这样的开关,并且能用它们组合出各种复杂的逻辑,会发生什么?这就是计算机的奥秘。
|
||||
|
||||
**从沙子到智能的层次结构:**
|
||||
|
||||
| 层级 | 名称 | 数量级 | 作用 | 类比 |
|
||||
|------|------|--------|------|------|
|
||||
| **1** | 晶体管 | 数十亿 | 最基本的开关单元 | 一个开关 |
|
||||
| **2** | 逻辑门 | 数亿 | 实现基本逻辑运算 | 开关组合 |
|
||||
| **3** | 功能单元 | 数百 | 实现特定功能(加法、存储等) | 功能模块 |
|
||||
| **4** | CPU 核心 | 1-128 | 完整的处理器 | 大脑 |
|
||||
| 层级 | 名称 | 数量级 | 作用 | 类比 |
|
||||
| ----- | -------- | ------ | ---------------------------- | -------- |
|
||||
| **1** | 晶体管 | 数十亿 | 最基本的开关单元 | 一个开关 |
|
||||
| **2** | 逻辑门 | 数亿 | 实现基本逻辑运算 | 开关组合 |
|
||||
| **3** | 功能单元 | 数百 | 实现特定功能(加法、存储等) | 功能模块 |
|
||||
| **4** | CPU 核心 | 1-128 | 完整的处理器 | 大脑 |
|
||||
|
||||
::: tip 📊 逐行解读这张表
|
||||
::: tip 逐行解读这张表
|
||||
**第1层(晶体管)**:这是最底层的"开关"。现代 CPU 使用的是 MOSFET(金属氧化物半导体场效应晶体管),它的特点是:给栅极加电压,源极和漏极之间就导通;不加电压,就断开。这就是"用电控制电"的开关。
|
||||
|
||||
**第2层(逻辑门)**:把晶体管组合起来,就能实现"与"、"或"、"非"等逻辑运算。比如 AND 门:两个输入都为 1 时输出才为 1。这就像两个串联的开关,必须都按下灯才会亮。
|
||||
@@ -37,12 +35,15 @@
|
||||
|
||||
## 1. 晶体管:数字世界的开关
|
||||
|
||||
<TransistorDemo />
|
||||
|
||||
### 1.1 什么是晶体管?
|
||||
|
||||
::: tip 💡 晶体管是什么?
|
||||
::: tip 晶体管是什么?
|
||||
**晶体管(Transistor)** 是一种半导体器件,它可以像开关一样控制电流的通断。
|
||||
|
||||
**生活类比**:想象一个水龙头:
|
||||
|
||||
- **水龙头**:你用手拧开关,控制水流
|
||||
- **晶体管**:用电压控制开关,控制电流
|
||||
|
||||
@@ -51,23 +52,24 @@
|
||||
|
||||
**晶体管的三个极:**
|
||||
|
||||
| 极 | 名称 | 作用 | 类比 |
|
||||
|---|------|------|------|
|
||||
| **源极 (Source)** | 电流入口 | 电流从这里进入 | 水管入口 |
|
||||
| **漏极 (Drain)** | 电流出口 | 电流从这里流出 | 水管出口 |
|
||||
| **栅极 (Gate)** | 控制端 | 控制是否导通 | 水龙头开关 |
|
||||
| 极 | 名称 | 作用 | 类比 |
|
||||
| ----------------- | -------- | -------------- | ---------- |
|
||||
| **源极 (Source)** | 电流入口 | 电流从这里进入 | 水管入口 |
|
||||
| **漏极 (Drain)** | 电流出口 | 电流从这里流出 | 水管出口 |
|
||||
| **栅极 (Gate)** | 控制端 | 控制是否导通 | 水龙头开关 |
|
||||
|
||||
### 1.2 晶体管如何表示 0 和 1?
|
||||
|
||||
计算机只认识 0 和 1,这和晶体管有什么关系?
|
||||
|
||||
::: tip 💡 用电压表示 0 和 1
|
||||
::: tip 用电压表示 0 和 1
|
||||
**核心思想**:用电压的高低来表示 0 和 1。
|
||||
|
||||
- **高电压(如 3.3V)**:表示 1
|
||||
- **低电压(如 0V)**:表示 0
|
||||
|
||||
这就像灯泡的亮和灭:
|
||||
|
||||
- 灯亮 = 1
|
||||
- 灯灭 = 0
|
||||
|
||||
@@ -80,15 +82,15 @@
|
||||
|
||||
**现代 CPU 的晶体管数量:**
|
||||
|
||||
| 年份 | CPU | 晶体管数量 | 制程工艺 |
|
||||
|------|-----|-----------|---------|
|
||||
| 1971 | Intel 4004 | 2,300 | 10μm |
|
||||
| 1993 | Intel Pentium | 310万 | 0.8μm |
|
||||
| 2006 | Intel Core 2 | 2.91亿 | 65nm |
|
||||
| 2020 | Apple M1 | 160亿 | 5nm |
|
||||
| 2023 | Apple M3 Max | 920亿 | 3nm |
|
||||
| 年份 | CPU | 晶体管数量 | 制程工艺 |
|
||||
| ---- | ------------- | ---------- | -------- |
|
||||
| 1971 | Intel 4004 | 2,300 | 10μm |
|
||||
| 1993 | Intel Pentium | 310万 | 0.8μm |
|
||||
| 2006 | Intel Core 2 | 2.91亿 | 65nm |
|
||||
| 2020 | Apple M1 | 160亿 | 5nm |
|
||||
| 2023 | Apple M3 Max | 920亿 | 3nm |
|
||||
|
||||
::: tip 💡 什么是制程工艺?
|
||||
::: tip 什么是制程工艺?
|
||||
**制程工艺**(如 5nm、3nm)指的是晶体管的尺寸。数字越小,晶体管越小,同样面积能容纳的晶体管越多。
|
||||
|
||||
- **5nm**:大约是 50 个原子的宽度
|
||||
@@ -110,21 +112,25 @@
|
||||
### 2.2 基本逻辑门详解
|
||||
|
||||
**AND 门(与门)**:
|
||||
|
||||
- **规则**:两个输入都为 1,输出才为 1
|
||||
- **生活类比**:串联的两个开关,必须都按下灯才亮
|
||||
- **应用**:判断"多个条件是否同时满足"
|
||||
|
||||
**OR 门(或门)**:
|
||||
|
||||
- **规则**:任一个输入为 1,输出就为 1
|
||||
- **生活类比**:并联的两个开关,按任意一个灯就亮
|
||||
- **应用**:判断"是否满足任一条件"
|
||||
|
||||
**NOT 门(非门)**:
|
||||
|
||||
- **规则**:输入和输出相反
|
||||
- **生活类比**:反相器,开变关、关变开
|
||||
- **应用**:取反操作
|
||||
|
||||
**XOR 门(异或门)**:
|
||||
|
||||
- **规则**:两个输入不同时输出 1
|
||||
- **生活类比**:判断"两个值是否不同"
|
||||
- **应用**:比较、加法运算
|
||||
@@ -135,15 +141,18 @@
|
||||
|
||||
::: tip 💡 加法器是怎么工作的?
|
||||
**半加器**:处理两个 1 位二进制数相加
|
||||
|
||||
- 输入:A、B(各 1 位)
|
||||
- 输出:和(S)、进位(C)
|
||||
- 公式:S = A XOR B,C = A AND B
|
||||
|
||||
**全加器**:处理两个 1 位二进制数相加,加上上一位的进位
|
||||
|
||||
- 输入:A、B、Cin(进位输入)
|
||||
- 输出:和(S)、Cout(进位输出)
|
||||
|
||||
**多位加法器**:把多个全加器级联起来
|
||||
|
||||
- 第 1 位加法器的进位输出,连接到第 2 位加法器的进位输入
|
||||
- 就像我们手算加法时"逢二进一"
|
||||
:::
|
||||
@@ -154,13 +163,15 @@
|
||||
|
||||
### 3.1 常见功能单元
|
||||
|
||||
| 单元 | 功能 | 组成 | 类比 |
|
||||
|------|------|------|------|
|
||||
| **加法器** | 做加法 | 多个全加器级联 | 计算器的加法功能 |
|
||||
| **多路选择器** | 选择数据 | AND 门 + OR 门 | 多选一开关 |
|
||||
| **译码器** | 解码指令 | 多个 AND 门 | 翻译器 |
|
||||
| **寄存器** | 存储数据 | 触发器(锁存器) | 临时笔记本 |
|
||||
| **计数器** | 计数 | 触发器级联 | 计分牌 |
|
||||
| 单元 | 功能 | 组成 | 类比 |
|
||||
| -------------- | -------- | ---------------- | ---------------- |
|
||||
| **加法器** | 做加法 | 多个全加器级联 | 计算器的加法功能 |
|
||||
| **多路选择器** | 选择数据 | AND 门 + OR 门 | 多选一开关 |
|
||||
| **译码器** | 解码指令 | 多个 AND 门 | 翻译器 |
|
||||
| **寄存器** | 存储数据 | 触发器(锁存器) | 临时笔记本 |
|
||||
| **计数器** | 计数 | 触发器级联 | 计分牌 |
|
||||
|
||||
<RegisterDemo />
|
||||
|
||||
### 3.2 寄存器:存储 1 位数据
|
||||
|
||||
@@ -168,6 +179,7 @@
|
||||
寄存器使用**触发器**电路来存储数据。触发器的特点是:一旦设置了状态,就能保持住,直到下一次改变。
|
||||
|
||||
**生活类比**:想象一个跷跷板:
|
||||
|
||||
- 推一下左边,左边就沉下去,右边翘起来
|
||||
- 即使你松手,跷跷板也会保持这个状态
|
||||
- 只有再推一下,才会改变状态
|
||||
@@ -187,17 +199,18 @@
|
||||
|
||||
CPU 执行一条指令,需要经过四个阶段:
|
||||
|
||||
| 阶段 | 名称 | 做什么 | 类比 |
|
||||
|------|------|--------|------|
|
||||
| **1** | 取指 (Fetch) | 从内存读取指令 | 从书架上取书 |
|
||||
| **2** | 解码 (Decode) | 分析指令要做什么 | 阅读书的内容 |
|
||||
| **3** | 执行 (Execute) | 执行运算 | 按书中的指示行动 |
|
||||
| 阶段 | 名称 | 做什么 | 类比 |
|
||||
| ----- | ----------------- | ---------------- | ------------------ |
|
||||
| **1** | 取指 (Fetch) | 从内存读取指令 | 从书架上取书 |
|
||||
| **2** | 解码 (Decode) | 分析指令要做什么 | 阅读书的内容 |
|
||||
| **3** | 执行 (Execute) | 执行运算 | 按书中的指示行动 |
|
||||
| **4** | 写回 (Write Back) | 把结果存回寄存器 | 把结果记在笔记本上 |
|
||||
|
||||
::: tip 💡 指令周期
|
||||
这四个阶段组成一个**指令周期**。CPU 不断重复这个周期,一条一条执行指令,就实现了"计算"。
|
||||
|
||||
现代 CPU 使用**流水线技术**,让多个指令的不同阶段并行执行:
|
||||
|
||||
- 第 1 条指令在执行时
|
||||
- 第 2 条指令在解码
|
||||
- 第 3 条指令在取指
|
||||
@@ -207,12 +220,12 @@ CPU 执行一条指令,需要经过四个阶段:
|
||||
|
||||
### 4.3 CPU 性能的关键指标
|
||||
|
||||
| 指标 | 含义 | 影响 | 典型值 |
|
||||
|------|------|------|--------|
|
||||
| **主频** | 每秒执行多少个时钟周期 | 主频越高,执行越快 | 3-5 GHz |
|
||||
| **核心数** | 独立的处理器数量 | 核心越多,并行能力越强 | 4-64 核 |
|
||||
| **缓存** | CPU 内部的高速存储 | 缓存越大,访问内存越少 | 8-64 MB |
|
||||
| **指令集** | CPU 能理解的指令集合 | 决定兼容性和功能 | x86、ARM |
|
||||
| 指标 | 含义 | 影响 | 典型值 |
|
||||
| ---------- | ---------------------- | ---------------------- | -------- |
|
||||
| **主频** | 每秒执行多少个时钟周期 | 主频越高,执行越快 | 3-5 GHz |
|
||||
| **核心数** | 独立的处理器数量 | 核心越多,并行能力越强 | 4-64 核 |
|
||||
| **缓存** | CPU 内部的高速存储 | 缓存越大,访问内存越少 | 8-64 MB |
|
||||
| **指令集** | CPU 能理解的指令集合 | 决定兼容性和功能 | x86、ARM |
|
||||
|
||||
---
|
||||
|
||||
@@ -220,23 +233,9 @@ CPU 执行一条指令,需要经过四个阶段:
|
||||
|
||||
让我们回顾一下从晶体管到 CPU 的完整路径:
|
||||
|
||||
```
|
||||
沙子(硅)
|
||||
↓ 提纯、切割
|
||||
硅晶圆
|
||||
↓ 光刻、蚀刻、掺杂
|
||||
晶体管(开关)
|
||||
↓ 组合
|
||||
逻辑门(AND、OR、NOT...)
|
||||
↓ 组合
|
||||
功能单元(加法器、寄存器...)
|
||||
↓ 组合
|
||||
CPU 核心(ALU、控制器、寄存器组...)
|
||||
↓ 编程
|
||||
软件应用
|
||||
```
|
||||
<EvolutionFlowDemo />
|
||||
|
||||
::: tip 💡 核心启示
|
||||
::: tip 核心启示
|
||||
**计算机的本质是"开关的组合"**。
|
||||
|
||||
- 一个开关做不了什么
|
||||
@@ -244,6 +243,7 @@ CPU 核心(ALU、控制器、寄存器组...)
|
||||
- 这就是"量变引起质变"的最好例证
|
||||
|
||||
理解这一点,你就会明白:
|
||||
|
||||
- 为什么计算机只认识 0 和 1
|
||||
- 为什么编程语言最终都要翻译成机器码
|
||||
- 为什么算法效率如此重要(因为每一步操作都需要大量晶体管参与)
|
||||
|
||||
@@ -1,475 +1,151 @@
|
||||
# 类型系统与编译原理入门
|
||||
|
||||
::: tip 🎯 核心问题
|
||||
**编程语言如何理解你的代码?** 当你写下 `int x = 10 + 5;` 时,编译器需要理解每个字符的含义、检查类型是否正确、优化代码、最终生成机器能执行的指令。本章带你理解这个神奇的过程。
|
||||
:::
|
||||
> 💡 **学习指南**:当你写下 `int x = 10 + 5;` 时,编译器是如何理解每个字符、检查类型是否正确、最终生成机器指令的?本章用两个核心概念——**类型系统**和**编译流程**——帮你理解编程语言背后的"翻译机制"。
|
||||
|
||||
---
|
||||
|
||||
## 0. 想象你在翻译一本书:
|
||||
## 0. 想象你是翻译官
|
||||
|
||||
- **识别单词**:把句子拆成一个个单词(词法分析)
|
||||
- **理解语法**:判断句子是否符合语法规则(语法分析)
|
||||
- **理解含义**:确保句子意思正确(语义分析)
|
||||
- **优化表达**:让句子更简洁(代码优化)
|
||||
- **翻译输出**:翻译成目标语言(代码生成)
|
||||
翻译一本书,你需要:
|
||||
|
||||
**编译器就是编程语言的"翻译官"**,将人类可读的代码转换为机器可执行的指令。
|
||||
1. **识别单词** — 把句子拆成一个个单词(词法分析)
|
||||
2. **理解语法** — 判断句子是否符合语法规则(语法分析)
|
||||
3. **理解含义** — 确保句子意思正确,类型不冲突(语义分析)
|
||||
4. **优化表达** — 让句子更简洁流畅(代码优化)
|
||||
5. **翻译输出** — 翻译成目标语言(代码生成)
|
||||
|
||||
**编译器就是编程语言的"翻译官"**,将你写的代码转换为机器能执行的指令。而**类型系统**就是翻译过程中的"语法检查器"——确保你不会把数字当文字用。
|
||||
|
||||
---
|
||||
|
||||
## 1. 类型系统:数据的交通规则
|
||||
|
||||
👇 动手点点看:探索四种类型系统的区别
|
||||
|
||||
<TypeSystemDemo />
|
||||
|
||||
::: tip 💡 一句话总结
|
||||
类型系统在两个维度上做选择:**何时检查**(编译时 vs 运行时)和**是否允许隐式转换**(强类型 vs 弱类型)。没有最好的组合,只有最适合的场景。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 1. 类型系统基础
|
||||
### 1.1 静态类型 vs 动态类型
|
||||
|
||||
### 1.1 什么是类型?
|
||||
| | 静态类型 | 动态类型 |
|
||||
|---|---|---|
|
||||
| **检查时机** | 编译时(还没运行就检查) | 运行时(跑到那行才检查) |
|
||||
| **发现 bug** | 早(写完就知道) | 晚(用户操作时才暴露) |
|
||||
| **灵活性** | 较低(类型固定) | 较高(类型可变) |
|
||||
| **IDE 支持** | 好(自动补全、重构) | 差(运行时才知道类型) |
|
||||
| **代表** | Java, TypeScript, Rust | Python, JavaScript, Ruby |
|
||||
|
||||
::: tip 💡 类型的本质
|
||||
类型是对数据的**分类**,规定了数据可以进行的操作。
|
||||
### 1.2 强类型 vs 弱类型
|
||||
|
||||
就像现实世界:
|
||||
- **整数**:可以加减乘除,但不能分割
|
||||
- **字符串**:可以拼接、截取,但不能直接运算
|
||||
- **布尔**:只有 true/false,用于逻辑判断
|
||||
:::
|
||||
**核心区别**:`"1" + 1` 会发生什么?
|
||||
|
||||
**基本数据类型**:
|
||||
- **强类型(Python)**:直接报错 `TypeError` — "你得明确告诉我怎么转"
|
||||
- **弱类型(JavaScript)**:悄悄转成 `"11"` — "我猜你想拼字符串"
|
||||
|
||||
| 类型 | 表示 | 占用空间 | 取值范围 |
|
||||
|------|------|---------|---------|
|
||||
| **整数** | int | 4 字节 | -2^31 到 2^31-1 |
|
||||
| **浮点数** | float | 4 字节 | 约 ±3.4 × 10^38 |
|
||||
| **双精度** | double | 8 字节 | 约 ±1.8 × 10^308 |
|
||||
| **字符** | char | 1 字节 | 0 到 255 |
|
||||
| **布尔** | bool | 1 字节 | true/false |
|
||||
弱类型的"好意"常常带来意想不到的 bug。
|
||||
|
||||
### 1.2 静态类型 vs 动态类型
|
||||
### 1.3 类型推断:两全其美
|
||||
|
||||
::: tip 💡 核心区别
|
||||
**静态类型**:变量类型在**编译时**确定
|
||||
**动态类型**:变量类型在**运行时**确定
|
||||
:::
|
||||
|
||||
**静态类型示例(Java)**:
|
||||
|
||||
```java
|
||||
String name = "Alice"; // 编译时确定 name 是 String 类型
|
||||
name = 123; // 编译错误!类型不匹配
|
||||
```
|
||||
|
||||
**动态类型示例(Python)**:
|
||||
|
||||
```python
|
||||
name = "Alice" # 运行时 name 是 str 类型
|
||||
name = 123 # 运行时 name 变成 int 类型
|
||||
print(type(name)) # <class 'int'>
|
||||
```
|
||||
|
||||
**对比分析**:
|
||||
|
||||
| 特性 | 静态类型 | 动态类型 |
|
||||
|------|---------|---------|
|
||||
| **类型检查时机** | 编译时 | 运行时 |
|
||||
| **错误发现** | 早(编译期) | 晚(运行时) |
|
||||
| **代码灵活性** | 低 | 高 |
|
||||
| **执行性能** | 高(编译优化) | 低(运行时检查) |
|
||||
| **IDE 支持** | 好(自动补全) | 差(运行时才知道类型) |
|
||||
| **代表语言** | Java, C++, Rust, TypeScript | Python, JavaScript, Ruby |
|
||||
|
||||
### 1.3 强类型 vs 弱类型
|
||||
|
||||
::: tip 💡 核心区别
|
||||
**强类型**:不允许隐式类型转换
|
||||
**弱类型**:允许隐式类型转换
|
||||
:::
|
||||
|
||||
**弱类型示例(JavaScript)**:
|
||||
|
||||
```javascript
|
||||
console.log("1" + 1) // "11" - 字符串拼接
|
||||
console.log("1" - 1) // 0 - 自动转数字
|
||||
console.log([] + []) // "" - 空数组转空字符串
|
||||
console.log(true + 1) // 2 - 布尔转数字
|
||||
```
|
||||
|
||||
**强类型示例(Python)**:
|
||||
|
||||
```python
|
||||
"1" + 1 # TypeError: can only concatenate str to str
|
||||
"1" - 1 # TypeError: unsupported operand type(s)
|
||||
```
|
||||
|
||||
**类型系统四象限**:
|
||||
|
||||
| | 强类型 | 弱类型 |
|
||||
|---|--------|--------|
|
||||
| **静态** | Java, Rust, Haskell | C, C++ |
|
||||
| **动态** | Python, Ruby | JavaScript, PHP |
|
||||
|
||||
### 1.4 类型推断
|
||||
|
||||
现代语言可以**自动推断**变量类型,结合静态类型的安全性和动态类型的简洁性:
|
||||
现代语言的类型推断让你**写着像动态语言,编译器检查像静态语言**:
|
||||
|
||||
```typescript
|
||||
// TypeScript
|
||||
let x = 1; // 推断为 number
|
||||
let arr = [1, 2, 3]; // 推断为 number[]
|
||||
let fn = (x) => x; // 推断为 (x: any) => any
|
||||
|
||||
// Rust
|
||||
let x = 1; // 推断为 i32
|
||||
let s = "hello"; // 推断为 &str
|
||||
let v = vec![1, 2]; // 推断为 Vec<i32>
|
||||
let x = 1 // 编译器自动推断为 number
|
||||
let arr = [1, 2, 3] // 推断为 number[]
|
||||
x = "hello" // ❌ 编译错误!类型不匹配
|
||||
```
|
||||
|
||||
你不用显式写类型声明,编译器也能帮你严格检查。
|
||||
|
||||
---
|
||||
|
||||
## 2. 编译原理基础
|
||||
## 2. 编译流程:从代码到机器码
|
||||
|
||||
### 2.1 编译器的任务
|
||||
|
||||
::: tip 💡 编译器做什么?
|
||||
编译器将**源代码**转换为**目标代码**,主要完成:
|
||||
|
||||
1. **理解代码**:分析源代码的结构和含义
|
||||
2. **检查正确性**:发现语法和语义错误
|
||||
3. **优化代码**:提高执行效率
|
||||
4. **生成代码**:输出目标机器的指令
|
||||
:::
|
||||
👇 动手点点看:输入代码,观察编译器的六步翻译过程
|
||||
|
||||
<CompilerDemo />
|
||||
|
||||
### 2.2 词法分析(Lexical Analysis)
|
||||
::: tip 💡 一句话总结
|
||||
编译器的六步流水线:源代码 → Token(词法分析)→ AST(语法分析)→ 带类型的 AST(语义分析)→ IR(中间代码)→ 优化后的 IR → 机器码。
|
||||
:::
|
||||
|
||||
**任务**:将源代码分解为**词法单元(Token)**
|
||||
---
|
||||
|
||||
**示例**:
|
||||
### 2.1 词法分析:拆出每个"单词"
|
||||
|
||||
```
|
||||
源代码: int x = 10 + 5;
|
||||
|
||||
词法单元:
|
||||
Token 流:
|
||||
[int] → 关键字
|
||||
[x] → 标识符
|
||||
[=] → 运算符
|
||||
[10] → 整数字面量
|
||||
[10] → 数字
|
||||
[+] → 运算符
|
||||
[5] → 整数字面量
|
||||
[5] → 数字
|
||||
[;] → 分隔符
|
||||
```
|
||||
|
||||
**词法分析器的工作**:
|
||||
|
||||
| 输入 | 处理 | 输出 |
|
||||
|------|------|------|
|
||||
| `int` | 匹配关键字表 | `KEYWORD(int)` |
|
||||
| `x` | 匹配标识符规则 | `IDENTIFIER(x)` |
|
||||
| `10` | 匹配数字规则 | `NUMBER(10)` |
|
||||
|
||||
### 2.3 语法分析(Syntax Analysis)
|
||||
|
||||
**任务**:根据语法规则,将 Token 流组织成**语法树(AST)**
|
||||
|
||||
**示例**:
|
||||
### 2.2 语法分析:构建语法树(AST)
|
||||
|
||||
```
|
||||
表达式: 1 + 2 * 3
|
||||
|
||||
语法树:
|
||||
+
|
||||
/ \
|
||||
1 *
|
||||
语法树: 为什么?
|
||||
+ 因为 * 的优先级
|
||||
/ \ 高于 +,所以
|
||||
1 * 2 * 3 先结合
|
||||
/ \
|
||||
2 3
|
||||
```
|
||||
|
||||
::: tip 💡 为什么是这棵树?
|
||||
根据运算优先级,`*` 优先级高于 `+`,所以 `2 * 3` 先结合。
|
||||
### 2.3 语义分析:检查"意思"是否正确
|
||||
|
||||
如果表达式是 `(1 + 2) * 3`,语法树会变成:
|
||||
| 检查内容 | 示例 | 结果 |
|
||||
|---|---|---|
|
||||
| 类型检查 | `int x = "hello"` | ❌ 类型不匹配 |
|
||||
| 作用域分析 | 使用未声明的变量 | ❌ 变量不存在 |
|
||||
| 类型推断 | `1 + 2.0` | ✅ 推断为 float |
|
||||
|
||||
```
|
||||
*
|
||||
/ \
|
||||
+ 3
|
||||
/ \
|
||||
1 2
|
||||
```
|
||||
:::
|
||||
### 2.4 代码优化:让程序跑得更快
|
||||
|
||||
**语法规则(文法)**:
|
||||
|
||||
```
|
||||
表达式 → 表达式 + 项 | 表达式 - 项 | 项
|
||||
项 → 项 * 因子 | 项 / 因子 | 因子
|
||||
因子 → 数字 | (表达式)
|
||||
```
|
||||
|
||||
### 2.4 语义分析(Semantic Analysis)
|
||||
|
||||
**任务**:检查语义正确性,进行类型检查
|
||||
|
||||
**主要工作**:
|
||||
|
||||
| 工作 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| **类型检查** | 检查类型是否匹配 | `int x = "hello";` → 错误 |
|
||||
| **作用域分析** | 检查变量是否声明 | 使用未声明变量 → 错误 |
|
||||
| **符号表构建** | 记录所有标识符信息 | 变量名、类型、作用域 |
|
||||
| **类型推断** | 推断表达式类型 | `1 + 2.0` → float |
|
||||
|
||||
**符号表示例**:
|
||||
|
||||
```
|
||||
int x = 10;
|
||||
float y = 3.14;
|
||||
string name = "Alice";
|
||||
|
||||
符号表:
|
||||
┌──────────┬────────┬─────────┐
|
||||
│ 名称 │ 类型 │ 作用域 │
|
||||
├──────────┼────────┼─────────┤
|
||||
│ x │ int │ global │
|
||||
│ y │ float │ global │
|
||||
│ name │ string │ global │
|
||||
└──────────┴────────┴─────────┘
|
||||
```
|
||||
|
||||
### 2.5 中间代码生成
|
||||
|
||||
**任务**:生成平台无关的中间表示(IR)
|
||||
|
||||
**三地址码示例**:
|
||||
|
||||
```
|
||||
源代码: int x = (a + b) * c;
|
||||
|
||||
三地址码:
|
||||
t1 = a + b
|
||||
t2 = t1 * c
|
||||
x = t2
|
||||
```
|
||||
|
||||
::: tip 💡 为什么需要中间代码?
|
||||
1. **平台无关**:一次编写,多平台编译
|
||||
2. **便于优化**:在 IR 层面进行优化
|
||||
3. **支持多语言**:不同语言可以编译到同一 IR
|
||||
|
||||
例如 LLVM IR 支持 C、C++、Rust、Swift 等多种语言。
|
||||
:::
|
||||
|
||||
### 2.6 代码优化
|
||||
|
||||
**任务**:提高代码执行效率
|
||||
|
||||
**常见优化技术**:
|
||||
|
||||
| 优化技术 | 说明 | 示例 |
|
||||
|---------|------|------|
|
||||
| **常量折叠** | 编译时计算常量表达式 | `10 + 5` → `15` |
|
||||
| **死代码消除** | 删除不会执行的代码 | `if (false) { ... }` → 删除 |
|
||||
| **内联展开** | 函数调用替换为函数体 | `add(1, 2)` → `1 + 2` |
|
||||
| **循环优化** | 减少循环开销 | 循环展开、循环不变量外提 |
|
||||
| **公共子表达式消除** | 避免重复计算 | `a+b` 计算一次,多次使用 |
|
||||
|
||||
**优化示例**:
|
||||
|
||||
```c
|
||||
// 优化前
|
||||
int x = 10 + 5; // 常量折叠
|
||||
int y = x * 2; // x 已知为 15
|
||||
if (false) { // 死代码
|
||||
printf("never");
|
||||
}
|
||||
|
||||
// 优化后
|
||||
int x = 15;
|
||||
int y = 30;
|
||||
// if 语句被删除
|
||||
```
|
||||
|
||||
### 2.7 目标代码生成
|
||||
|
||||
**任务**:生成目标机器的机器码
|
||||
|
||||
**汇编代码示例**:
|
||||
|
||||
```asm
|
||||
; int x = 15;
|
||||
mov eax, 15
|
||||
mov dword ptr [x], eax
|
||||
|
||||
; int y = 30;
|
||||
mov eax, 30
|
||||
mov dword ptr [y], eax
|
||||
```
|
||||
|
||||
**代码生成的主要任务**:
|
||||
|
||||
| 任务 | 说明 |
|
||||
|------|------|
|
||||
| **指令选择** | 选择合适的机器指令 |
|
||||
| **寄存器分配** | 决定哪些变量放在寄存器 |
|
||||
| **指令调度** | 安排指令顺序,提高流水线效率 |
|
||||
| 优化技术 | 优化前 | 优化后 |
|
||||
|---|---|---|
|
||||
| 常量折叠 | `x = 10 + 5` | `x = 15` |
|
||||
| 死代码消除 | `if (false) { ... }` | 直接删除 |
|
||||
| 常量传播 | `y = x * 2`(x=15) | `y = 30` |
|
||||
|
||||
---
|
||||
|
||||
## 3. 编译型 vs 解释型 vs JIT
|
||||
|
||||
### 3.1 编译型语言
|
||||
程序写完后,有三种"翻译方式"让它运行:
|
||||
|
||||
**流程**:源代码 → 编译器 → 机器码 → 执行
|
||||
| | 编译型 | 解释型 | JIT 即时编译 |
|
||||
|---|---|---|---|
|
||||
| **过程** | 先编译成机器码,再执行 | 边读边执行 | 先解释,热点代码再编译 |
|
||||
| **速度** | 最快 | 最慢 | 中等(热点代码接近编译型) |
|
||||
| **启动** | 慢(需编译) | 快(直接运行) | 中等(需预热) |
|
||||
| **跨平台** | 需要重新编译 | 天然跨平台 | 跨平台 |
|
||||
| **代表** | C, Rust, Go | Python, Ruby | Java, JavaScript (V8) |
|
||||
|
||||
```
|
||||
main.c → [编译器] → main.exe → [CPU] → 执行
|
||||
```
|
||||
|
||||
**特点**:
|
||||
- ✅ 执行速度快
|
||||
- ✅ 编译期发现错误
|
||||
- ❌ 编译时间长
|
||||
- ❌ 跨平台需要重新编译
|
||||
|
||||
**代表语言**:C, C++, Rust, Go
|
||||
|
||||
### 3.2 解释型语言
|
||||
|
||||
**流程**:源代码 → 解释器 → 逐行执行
|
||||
|
||||
```
|
||||
main.py → [解释器] → 逐行解释执行
|
||||
```
|
||||
|
||||
**特点**:
|
||||
- ✅ 跨平台
|
||||
- ✅ 开发调试快
|
||||
- ❌ 执行速度慢
|
||||
- ❌ 运行时才能发现错误
|
||||
|
||||
**代表语言**:Python, Ruby, PHP
|
||||
|
||||
### 3.3 JIT(即时编译)
|
||||
|
||||
**流程**:源代码 → 字节码 → JIT 编译 → 执行
|
||||
|
||||
```
|
||||
源代码 → [编译器] → 字节码 → [JIT] → 机器码 → 执行
|
||||
```
|
||||
|
||||
**工作原理**:
|
||||
1. 先将源代码编译成字节码(中间代码)
|
||||
2. 解释器逐行执行字节码
|
||||
3. 发现热点代码(频繁执行),JIT 编译成机器码
|
||||
4. 后续直接执行机器码
|
||||
|
||||
**特点**:
|
||||
- ✅ 兼顾性能和跨平台
|
||||
- ✅ 热点代码执行快
|
||||
- ❌ 启动慢(需要预热)
|
||||
- ❌ 内存占用大
|
||||
|
||||
**代表语言**:Java (JVM), JavaScript (V8), Python (PyPy)
|
||||
::: tip 💡 为什么 JavaScript 这么快?
|
||||
V8 引擎的 JIT 编译器会监测哪些代码被频繁执行(热点代码),然后把它们编译成高度优化的机器码。所以虽然 JavaScript 是"解释型语言",但在 V8 中它的性能可以接近编译型语言。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 4. 实践:手写简单解释器
|
||||
|
||||
### 4.1 目标
|
||||
|
||||
实现一个简单的计算器,支持加减乘除:
|
||||
|
||||
```
|
||||
输入: 1 + 2 * 3
|
||||
输出: 7
|
||||
```
|
||||
|
||||
### 4.2 词法分析器
|
||||
|
||||
```python
|
||||
import re
|
||||
|
||||
Token = namedtuple('Token', ['type', 'value'])
|
||||
|
||||
def tokenize(code):
|
||||
tokens = []
|
||||
for match in re.finditer(r'\d+|[+\-*/()]', code):
|
||||
value = match.group()
|
||||
if value.isdigit():
|
||||
tokens.append(Token('NUMBER', int(value)))
|
||||
else:
|
||||
tokens.append(Token(value, value))
|
||||
return tokens
|
||||
|
||||
# 测试
|
||||
print(tokenize('1 + 2 * 3'))
|
||||
# [Token(type='NUMBER', value=1), Token(type='+', value='+'), ...]
|
||||
```
|
||||
|
||||
### 4.3 语法分析器
|
||||
|
||||
```python
|
||||
class Parser:
|
||||
def __init__(self, tokens):
|
||||
self.tokens = tokens
|
||||
self.pos = 0
|
||||
|
||||
def parse(self):
|
||||
return self.expr()
|
||||
|
||||
def expr(self):
|
||||
result = self.term()
|
||||
while self.current() in ('+', '-'):
|
||||
op = self.consume()
|
||||
right = self.term()
|
||||
if op == '+':
|
||||
result += right
|
||||
else:
|
||||
result -= right
|
||||
return result
|
||||
|
||||
def term(self):
|
||||
result = self.factor()
|
||||
while self.current() in ('*', '/'):
|
||||
op = self.consume()
|
||||
right = self.factor()
|
||||
if op == '*':
|
||||
result *= right
|
||||
else:
|
||||
result //= right
|
||||
return result
|
||||
|
||||
def factor(self):
|
||||
token = self.consume()
|
||||
if token.type == 'NUMBER':
|
||||
return token.value
|
||||
elif token.value == '(':
|
||||
result = self.expr()
|
||||
self.consume() # )
|
||||
return result
|
||||
```
|
||||
|
||||
### 4.4 完整解释器
|
||||
|
||||
```python
|
||||
def evaluate(code):
|
||||
tokens = tokenize(code)
|
||||
parser = Parser(tokens)
|
||||
return parser.parse()
|
||||
|
||||
print(evaluate('1 + 2 * 3')) # 7
|
||||
print(evaluate('(1 + 2) * 3')) # 9
|
||||
print(evaluate('10 - 2 * 3')) # 4
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 总结
|
||||
## 4. 总结
|
||||
|
||||
::: tip 📚 核心要点
|
||||
1. **类型系统**:静态/动态、强/弱类型,影响代码安全和灵活性
|
||||
2. **编译流程**:词法分析 → 语法分析 → 语义分析 → 中间代码 → 优化 → 代码生成
|
||||
3. **执行方式**:编译型快但需编译,解释型慢但灵活,JIT 兼顾两者
|
||||
4. **实践价值**:理解编译原理有助于写出更好的代码
|
||||
1. **类型系统**:静态/动态决定检查时机,强/弱决定是否允许隐式转换
|
||||
2. **编译六步**:词法分析 → 语法分析 → 语义分析 → 中间代码 → 优化 → 代码生成
|
||||
3. **三种执行**:编译型快但需编译,解释型灵活但慢,JIT 兼顾两者
|
||||
4. **类型推断**:现代语言让你享受动态语言的简洁和静态语言的安全
|
||||
:::
|
||||
|
||||
**下一步学习**:
|
||||
|
||||
Reference in New Issue
Block a user