万物基于C语言:丹尼斯·里奇与C语言帝国的兴起与永恒
引言:一个低调到没有维基百科词条照片的人
2011年10月12日,丹尼斯·里奇(Dennis MacAlistair Ritchie)在家中去世,享年70岁。
《纽约时报》为他写的讣告标题是:
“丹尼斯·里奇,1970年代改变世界的沉默巨人,于70岁辞世。”
“沉默"这个词用得极其准确。里奇不像乔布斯那样被拍成电影,不像比尔·盖茨那样出现在每一本商业教科书里,甚至不像他的同事肯·汤普森(Ken Thompson)那样经常被技术媒体采访。他只是安静地写代码,安静地改变了这个世界。
而他创造的C语言,至今仍是全球使用最广泛的编程语言之一——不是因为它最新、最炫、最适合AI,而是因为它离计算机的本质最近。
第一章:C语言诞生之前:UNIX与BCPL之间的空白
1.1 1969年,贝尔实验室的夏天
要理解C语言为什么长成这样,必须先理解它诞生的背景。
1969年,贝尔实验室的几位研究员从麻省理工学院(MIT)借来一台DEC PDP-7小型机,准备开发一个全新的操作系统。这台机器只有4KB内存,没有磁盘,显示器是一台打字机式的终端。
肯·汤普森(Ken Thompson)在上面写了一个操作系统。最初的版本只用汇编写成——这在当时是标准做法。但汇编的问题是:换一个硬件平台,所有代码都要重写。
汤普森想要一种更高级的语言来重写这个操作系统。他找到了一个候选:BCPL(Basic Combined Programming Language),由剑桥大学的Martin Richards于1967年开发。
汤普森从BCPL中提取了核心思想,做了简化,创造了B语言。1970年,他用B语言重写了UNIX的第一版。
1.2 B语言的问题:它太弱了
B语言的设计初衷是让程序员用更少的代码表达更多的意思,但B语言有一个致命的弱点:它是"解释型"的。
什么叫解释型?B语言的程序运行时,需要一个B语言的"解释器"先把代码逐行翻译成机器能懂的指令,再执行。这意味着:
1
2
3
4
|
每个B程序运行:
→ 需要先启动B解释器(占大量内存)
→ 解释器再逐行翻译程序(速度慢)
→ 浪费了PDP-11本就不多的内存和算力
|
而且B语言只有一种数据类型:字(Word),一个"字"的大小取决于机器的硬件,可能是8位、16位或32位。这导致程序完全不可移植。
汤普森在1970年拿到了新的DEC PDP-11,这台机器有24KB内存。UNIX团队需要一种语言,能在有限的硬件资源下写出高效的系统程序。
C语言就是在这样的背景下诞生的——UNIX团队需要一种"比汇编强,但比B语言快"的语言。
第二章:C语言诞生:UNIX的"官方配套语言”
2.1 丹尼斯·里奇的出场
丹尼斯·里奇1941年9月9日出生于纽约州布兰克斯维尔,父亲是贝尔实验室的电子工程师。1968年,里奇在哈佛大学应用数学系获得硕士学位(博士读到一半退出了),然后加入了贝尔实验室。
1972年,里奇在B语言的基础上,动手重写UNIX。过程中他创造了C语言。
为什么叫C?因为B语言之后,顺着字母表,下一个就是C。
C语言对B语言做了关键性的改进:
1
2
3
4
5
6
7
8
9
10
11
|
B语言的致命缺陷:
✗ 只有一种数据类型(字)
✗ 解释型执行,速度慢
✗ 没有结构体,复杂数据难组织
C语言的关键创新:
✓ 引入基本数据类型(char、int、float等)
✓ 编译型语言,生成直接可执行的机器码
✓ 引入结构体(struct),可以自定义复杂数据类型
✓ 指针(pointer),直接操作内存地址
✓ 预处理宏(#define)
|
2.2 C语言的第一行代码
C语言诞生之初,是为了UNIX操作系统服务的。实际上,UNIX本身就是用C语言重写的第一个大型软件系统。
UNIX + C的组合具有划时代的意义:
1
2
3
4
5
6
7
8
|
传统操作系统:用汇编写(完全不可移植)
→ 换一台机器,代码全部重写
→ 只有极少数人能维护和修改
UNIX + C:用C语言写(可移植)
→ 换一台机器,只需要重写硬件相关的1000行代码(硬件抽象层)
→ 其余99%的代码直接复用
→ 大幅降低了操作系统移植和演进的成本
|
这个"可移植性"的理念,后来深刻影响了整个软件工业。
2.3 C语言与UNIX的共生关系
UNIX和C语言是一对"共生物种"——谁也离不开谁:
1
2
3
4
5
6
7
8
9
|
UNIX需要C语言:
→ C语言足够低级,可以直接操作硬件(系统调用)
→ C语言足够高级,代码比汇编更易读、更易维护
→ C语言编译出的程序运行效率极高,接近汇编
C语言需要UNIX:
→ UNIX是C语言的第一个大型"试验场"
→ UNIX证明了C语言可以写出真实的生产级操作系统
→ UNIX的普及带动了C语言的传播
|
第三章:丹尼斯·里奇——C语言帝国背后的安静大脑
3.1 一个不喜欢宣传的人
丹尼斯·里奇在贝尔实验室工作了近40年(1968-2007),但他几乎从不接受采访,不参加商业会议,甚至在公开场合露面也很少。
他的同事回忆说,里奇经常穿着T恤和牛仔裤上班,“看起来就像是随时要去仓库搬箱子的人”。他更喜欢待在办公室里写代码,或者和同事在走廊里讨论技术问题。
有一次,贝尔实验室为他申请了一个专利(用于UNIX的文件系统),但里奇主动把自己的名字从发明人列表里删除了——他觉得UNIX是整个团队的成果,不应该属于任何一个人。
这种低调到骨子里的人,在硅谷的"个人英雄主义"叙事中几乎是异类。
3.2 里奇的设计哲学
C语言为什么长成这个样子,而不是别的样子?这背后是里奇深思熟虑的设计哲学。
哲学一:信任程序员
C语言的设计者假设使用C语言的人是懂行的专业人士。它不会在你不小心访问数组越界时"自动保护",不会在你不小心除以零时报错——这些在其他语言里可能是安全特性,但在C里,这些操作的结果是"未定义行为"(Undefined Behavior)。
1
2
3
4
5
|
// C语言:不保护数组越界
int arr[5] = {1, 2, 3, 4, 5};
printf("%d\n", arr[10000]); // 越界访问,编译器不报错
// 运行结果:不知道是多少(未定义行为)
// 可能:输出一个随机数、崩溃、或者恰好是内存里的另一个变量
|
里奇认为:“程序员应该知道自己在做什么。“这个哲学让C语言变得极快——因为不需要额外的安全检查。
哲学二:不给程序员设限
C语言的设计原则是:给程序员足够的权力去操作系统,不做任何限制。 你可以读写任意内存地址,可以把任何值强制转换成任何类型,可以用指针做任何事——包括做正确的事和做危险的事。
1
2
3
4
5
|
// C语言的强制类型转换:你想怎么转就怎么转
int i = 65;
char *p = (char *)&i; // 把int指针强转为char指针
printf("%c\n", *p); // 可能输出 'A',也可能输出其他字符
// 编译器不会阻止你——它相信你知道你在做什么
|
哲学三:简洁、最小化、不做多余的设计
C语言只有32个关键字。相比之下,Python有35个关键字,Java有50个,C++有75个。里奇相信:语言本身应该小而精,功能由标准库和程序员来实现。
第四章:C语言帝国:万物基于C的版图
4.1 操作系统——C语言的第一个帝国
这是C语言最核心的影响力:几乎所有现代操作系统的内核,都是用C语言写的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
C语言操作系统家族:
UNIX系列:
├── AT&T UNIX(原始版,C语言编写)
├── BSD系列
│ ├── FreeBSD(Netflix等公司用它处理网络流量)
│ ├── macOS / iOS(Darwin内核)
│ └── OpenBSD(安全加固)
└── Solaris(AURORA、甲骨文内部使用)
Linux系列:
└── Linux内核(2026年:3000万行代码,全部C语言)
└── Android系统底层(基于Linux内核)
└── 服务器、嵌入式设备、超级计算机、桌面
Windows:
└── Windows内核(C语言 + C++混合)
└── NT内核:进程管理、内存管理、文件系统
└── Windows API:应用程序接口层
其他:
└── 嵌入式RTOS(FreeRTOS、VxWorks)
└── 智能手表OS、路由器OS、交换机OS
|
Linux内核的3000万行C代码是什么概念?
1
2
3
4
5
6
7
8
|
如果把这3000万行代码打印成书:
→ 每页50行,每页约0.5cm厚
→ 共600万页
→ 叠加高度:30公里
→ 从深圳地面叠到大气层边缘
一个人读这些代码(每行1分钟):
→ 读完需要:3000万分钟 = 57年(不吃不喝不睡)
|
Linux是全球最大的开源协作项目,由来自全球数万名开发者贡献,核心代码至今仍几乎100%由C语言写成。这是C语言帝国最坚固的城池。
4.2 编程语言——C语言的"殖民地”
当今最流行的编程语言,几乎没有例外,全部或部分由C语言或其衍生语言实现:
1
2
3
4
5
6
7
8
9
10
11
|
C语言的"语言家族":
C(1972)
│
┌─────────┬───────┼────────┬─────────┐
↓ ↓ ↓ ↓ ↓
C++ Java Python Perl Objective-C
(1979) (1995) (1991) (1987) (1984)
JavaScript C# Go Rust PHP
(1995) (2000) (2009) (2010) (1995)
|
Python——用C语言写的"胶水语言”
Python的官方实现CPython,核心解释器完全由C语言编写:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// CPython的Python/ceval.c(简化示例)
case TARGET(BINARY_ADD): {
PyObject *right = POP();
PyObject *left = TOP();
PyObject *sum = PyNumber_Add(left, right);
Py_DECREF(right);
Py_DECREF(left);
if (sum == NULL)
goto error;
SET_TOP(sum);
DISPATCH();
}
// Python的高级语法(a + b),底层是C语言的函数调用
// 内存管理(Py_DECREF/Py_INCREF)、对象系统全部由C实现
|
Python的标准库中,有大量模块是C语言编写的:
cmath、math → C语言的数学库封装
json(部分) → C语言的高性能JSON解析器
hashlib → C语言的OpenSSL封装
re → C语言的正则表达式引擎
Java——虚拟机用C语言实现
Java的HotSpot虚拟机(JVM)是用C++写的,而C++最初也是C的超集。Java代码首先被编译成字节码(Bytecode),然后由JVM解释执行。
1
2
3
4
5
6
7
|
Java源代码(.java)
↓ javac 编译器
Java字节码(.class)
↓
JVM(用C++编写,而C++的STL部分用C实现)
↓
机器码
|
JavaScript——Chrome V8引擎是C++的,但……
Google Chrome的JavaScript引擎V8,将JS代码编译成机器码,核心部分由C++编写(V8本身是C++项目)。但V8依赖的底层系统调用、内存管理、垃圾回收器的基础设施,仍然是C语言的领地。
Go——Google内部创造的C语言继承人
Go语言(又称Golang)由Google的罗伯特·格瑞史莫、罗布·派克、肯·汤普森(又是他!)于2009年创造。Go的编译器 runtime、部分标准库是用Go本身写的,但:
1
2
3
4
5
6
7
|
Go的组成部分:
├── 编译器(go tool compile):Go语言本身写成
├── 运行时(runtime):部分Go写成,部分C写成
├── GC(垃圾回收器):Go写成
└── 标准库的系统调用层:用C语言调用操作系统
↓
Linux/BSD/Windows的底层系统调用(C语言API)
|
Rust——C语言的"安全进化"
Rust语言的设计目标之一是在保留C语言性能和控制力的同时,消除所有不安全的内存操作。Rust的编译器(rustc)本身是用Rust和C++混合写成的。
1
2
3
4
|
Rust的架构:
├── 编译器前端:Rust写成(语法分析、类型检查)
├── 编译器后端(LLVM):C++写成
└── 标准库:Rust写成(安全抽象)
|
4.3 数据库——数据王国的C语言基石
今天几乎所有主流数据库,都是用C语言或其衍生语言实现的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
数据库的C语言版图:
关系型数据库:
├── MySQL(全部C语言写成,C API是其灵魂)
├── PostgreSQL(C语言写成,部分C++)
├── SQLite(全部C语言,约15万行代码)
│ → 世界上最广泛部署的数据库引擎
│ → 每个iPhone/Android手机里都有它
│ → 浏览器内置的本地存储
└── Oracle Database(C语言 + 汇编混合)
NoSQL数据库:
├── Redis(C语言写成,高性能内存数据库)
├── MongoDB(C++语言写成,但底层存储引擎 WiredTiger 用C)
├── Cassandra(C++ + Java,存储层有C)
└── LevelDB(Google的C++实现,被多款区块链采用)
数据库中间件:
└── ClickHouse(俄罗斯第一大电商Yandex出品,C++写成)
|
SQLite的故事:SQLite是D.理查德·希普(D. Richard Hipp)一个人从2000年开始开发的,全世界目前有超过1万亿(1 trillion)个SQLite实例在运行——每一部智能手机、每一个浏览器、每一部汽车的车载娱乐系统里都有它。15万行纯C代码,支撑了人类信息存储的基石。
4.4 嵌入式与物联网——C语言的"隐形帝国"
C语言在嵌入式领域的地位,是其他任何语言都无法撼动的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
嵌入式C语言应用:
汽车电子:
├── 发动机控制单元(ECU)
├── 防抱死制动系统(ABS)
├── 安全气囊控制
├── 车载信息娱乐系统(部分)
│ → 实时操作系统:QNX(全部C)、FreeRTOS(C)
│ → CAN总线协议栈(C语言实现)
消费电子:
├── 智能手表固件(RTOS,C语言)
├── 数码相机固件(C + 汇编)
├── 路由器/交换机固件(C语言)
│ → OpenWrt:Linux内核 + C语言应用层
│ → Cisco IOS:纯C语言
工业控制:
├── PLC(可编程逻辑控制器)
├── 工业机器人控制
├── 电力系统SCADA
微控制器(MCU):
├── STM32系列(ARM Cortex-M)
├── ESP32(Wi-Fi + 蓝牙物联网芯片)
└── Arduino(AVR单片机)
→ 全部用C语言编程(部分支持汇编)
|
为什么嵌入式系统非C不可?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
为什么不用Python?因为Python太慢了(解释执行)
为什么不用Java?因为Java需要JVM,JVM太重(至少几MB内存)
为什么不用Go?因为Go的运行时(runtime)太大(几MB)
嵌入式系统的资源:
→ 微控制器:2KB RAM,32KB Flash
→ 智能传感器:1KB RAM,16KB ROM
→ 实时系统:确定性延迟要求,微秒级响应
C语言的优势:
→ 直接操作硬件寄存器
→ 零运行时开销(No Runtime)
→ 生成的机器码极其精简(几十字节就能跑)
→ 编译后的二进制文件可以小到几KB
|
4.5 解释器与虚拟机——C语言是语言之母
主流编程语言的运行时,有两个共同特点:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
主流运行时技术栈:
Python → CPython(纯C实现)
Ruby → CRuby/MRI(纯C实现)
PHP → Zend Engine(C语言实现)
Perl → Perl解释器(C语言实现)
Lua → Lua解释器(纯C实现,极其轻量,游戏开发最爱)
Ruby → JRuby → JVM → 底层仍是C
对比:完全不需要C的语言
→ 纯解释型(无编译无虚拟机):Shell脚本
→ 自举(自己写自己):Go、Rust(可以不用C编译)
→ WebAssembly:可以不依赖C,直接编译到Wasm
|
4.6 编译器工具链——C语言自己编译自己
C语言的编译工具链,是一个完美的"自举"体系:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
C语言编译工具链(以GCC为例):
源代码(C/C++语言)
↓
GCC前端(预处理、语法分析、语义分析)
↓
中间表示(IR,GIMPLE)
↓
GCC后端(目标代码生成)
↓
汇编(as)
↓
目标文件(.o,机器码)
↓
链接器(ld,链接库文件)
↓
可执行文件(机器码)
整个GCC工具链本身,也是用C语言写成的。
这意味着:你用C语言写的C语言编译器,可以编译它自己。
这叫"自举"(Bootstrap)。
|
第五章:C语言的遗产与深层影响
5.1 C语言设计哲学对后世的影响
C语言的遗产不仅仅是代码,更是一种设计哲学:
遗产一:信任程序员,而非限制程序员
这个哲学深刻影响了后来的C++、Rust(作为对C哲学的反思和超越)。Rust的设计者意识到"信任程序员"在系统编程中会导致太多内存安全问题,于是发明了"所有权系统"(Ownership)——一种在编译时强制内存安全的机制。这是C语言哲学的"下一代进化"。
遗产二:显式优于隐式
C语言要求程序员显式地声明所有操作:
1
2
|
int *p = NULL; // 显式声明一个整型指针,初始化为空
*p = 42; // 显式解引用,写入内存
|
这与后来的许多语言(Python、Ruby)不同——那些语言有自动内存管理,程序员不需要显式操作内存。C语言的显式哲学告诉程序员:你对系统有完全的控制权,也对系统的所有行为负有完全的责任。
遗产三:零成本抽象
C++的设计者Bjarne Stroustrup创造C++时,提出的核心承诺是:“如果你不用某个特性,你就不需要为它付出代价。” 这是从C语言继承来的哲学——高性能、不做多余的事。这个承诺后来深刻影响了Go语言(interface{}零成本抽象)和Rust语言(零成本抽象原则)。
5.2 C语言家族族谱
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
C语言家族简谱:
1972 — C语言(丹尼斯·里奇,贝尔实验室)
│
├─→ 1979 — C with Classes → C++(Bjarne Stroustrup)
│ │
│ ├─→ 游戏引擎(Unreal Engine 5)
│ ├─→ 操作系统(Windows核心)
│ ├─→ 数据库(MySQL client API)
│ └─→ Web浏览器(Chrome、Firefox)
│
├─→ 1983 — Objective-C(Brad Cox)
│ │
│ └─→ macOS / iOS 应用开发
│
├─→ 1995 — Java(James Gosling)
│ │ (受C++影响,语法相似)
│ └─→ 企业级应用、Android开发
│
├─→ 1995 — JavaScript(Brendan Eich)
│ │ (语法受C/Java影响)
│ └─→ Web前端、后端(Node.js)
│
├─→ 2000 — C#(Anders Hejlsberg)
│ │ (C/C++/Java语法影响)
│ └─→ Windows应用、游戏(Unity)
│
├─→ 2009 — Go(Google,肯·汤普森参与创建)
│ └─→ 云原生、微服务、容器(Docker/Kubernetes)
│
└─→ 2010 — Rust(Mozilla Graydon Hoare)
└─→ 追求内存安全的新一代系统语言
|
5.3 C语言的局限性——里奇自己承认的错误
里奇晚年接受采访时,坦诚承认了C语言的一些设计缺陷:
缺陷一:数组和指针不做边界检查
1
2
3
|
int arr[10];
arr[1000000] = 42; // 不报错,直接写入未知内存区域
// 这是无数安全漏洞的根源(缓冲区溢出)
|
缺陷二:运算符优先级混乱
1
2
3
4
|
// 这个表达式你能直接看出结果吗?
if (flags & FLAG != 0) { ... }
// 实际上:flags & (FLAG != 0),而不是(flags & FLAG) != 0
// C语言的优先级规则非常反直觉,且历史上留下了一些错误
|
缺陷三:预处理宏的文本替换过于简单
1
2
3
4
|
#define SQUARE(x) x * x
int a = 5;
int b = SQUARE(a + 1); // 结果是 5 + 1 * 5 + 1 = 11,不是36!
// 应该用内联函数替代宏,但C89时代没有内联函数
|
缺陷四:字符串处理需要手动管理
1
2
3
4
5
6
|
// C语言字符串:手动管理内存,容易出错
char *s1 = "hello";
char *s2 = malloc(6);
strcpy(s2, s1); // 如果忘记malloc就会崩溃
free(s2); // 如果忘记free就内存泄漏
// 里奇说:"如果能重来,字符串处理会是我第一个重做的部分"
|
第六章:C语言帝国的一天——你在不知不觉中使用C语言多少次
让我们用一个普通人的一天,来展示C语言帝国的版图有多庞大:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
早上8:00 — 醒来
├── 手机的基带处理器 → 运行C语言的固件
├── Android系统底层 → Linux内核(C语言)
└── 闹钟App → Java/Kotlin → JVM → 最终调用的系统服务由C写成
早上8:30 — 开车上班
├── ABS防抱死系统 → C语言的ECU固件
├── 车载导航 → 嵌入式Linux(C语言)
├── 胎压监测 → C语言的传感器固件
└── 汽车CAN总线 → C语言的车载网络协议栈
上午9:00 — 打开电脑工作
├── macOS/Windows启动 → 内核由C语言写成
├── VS Code编辑器 → Electron框架 → Node.js → V8引擎 → C++
├── Git操作 → C语言写成的Git
└── MySQL数据库查询 → C语言写成的数据库
上午10:00 — 打开浏览器
├── Chrome浏览器 → V8引擎(C++)→ C语言基础服务
├── HTTP请求 → OpenSSL库(C语言)→ TLS加密
└── 浏览网页 → 每个网页由浏览器渲染引擎(C++)处理
中午12:00 — 数据库操作
├── MySQL查询 → C语言引擎
├── Redis缓存 → C语言引擎
└── 消息队列 → Kafka(Java/Scala)→ 底层仍是C语言/Linux
下午2:00 — 部署服务到云服务器
├── Docker容器 → Go语言外壳 → Linux内核(C语言)
├── Kubernetes编排 → Go语言 → Linux(C语言)
└── Nginx反向代理 → C语言写成的HTTP服务器
晚上8:00 — 回家
├── 智能电视 → 嵌入式Linux(C语言)
├── 游戏机 → 底层固件(C/汇编)
├── Wi-Fi路由器 → OpenWrt Linux(C语言)
└── 你的智能手表 → FreeRTOS(C语言)
这一天,你直接或间接使用了多少行C语言代码?
→ Linux内核:3000万行
→ OpenSSL:50万行
→ Nginx:10万行
→ MySQL:300万行
→ SQLite:15万行
→ ……加上其他所有固件、驱动、协议栈
→ 总计:可能超过1亿行
|
结语:丹尼斯·里奇留下的不只是代码
2011年10月,里奇去世的消息在技术社区传开时,一位 Hacker News 的用户写道:
“我用的每一台设备、每一行代码、每一个字节的互联网流量背后,都有他的指纹。但我在今天之前甚至不知道他是谁。”
这句话准确地描述了里奇和C语言的处境:它们是现代文明的沉默基础设施,像空气一样无处不在,以至于我们几乎忘记了它们的存在。
C语言帝国不需要皇帝——因为它没有中心,没有董事会,没有CEO。Linux内核的维护者们、MySQL的贡献者们、Python解释器的开发者们、嵌入式工程师们——他们都是这个帝国的公民,而他们共同使用的语言,是丹尼斯·里奇在1972年的贝尔实验室里,用B语言改造而来的一种编程工具。
他当时大概没有想到,这个"用来写UNIX的工具",会在接下来的半个世纪里,成为支撑整个数字世界的底层基石。
关联文章:
- 《从晶体管到AI:一张图揭示计算机科学所有知识的神秘联系》——计算机科学知识地图中的语言与编译原理
- 《BGE-M3向量模型完全指南:Obsidian本地RAG与FastGPT配置实战》——Python(底层C语言)驱动的大模型应用