编码之神_编程语言群星闪耀时

序幕:巴比伦塔的诅咒

"我们就像是在试图用巴比伦的语言与上帝对话——问题不在于上帝听不懂,而在于我们自己都不知道自己在说什么。"

——艾兹格·迪杰斯特拉(Edsger W. Dijkstra),1975年图灵奖得主


一、当人类第一次命令机器思考

想象一下,你站在1946年2月宾夕法尼亚大学摩尔电气工程学院的一间巨大房间里。

现在是凌晨两点,但没有人想睡觉。你脚下的水磨石地板微微震动,空气里弥漫着一股奇怪的气味——像是烧焦的金属混合着臭氧,又夹杂着纸带上油墨的甜腻。头顶上,几十盏日光灯管嗡嗡作响,但它们的光亮远不够驱散这个房间的阴暗,因为真正照亮这里的,是另外一万八千个光源。

真空管

一万八千个玻璃真空管,每一个都有你的拳头那么大,每一个都在发出橘红色的微光,像一万八千只不眠的眼睛。它们排列在巨大的金属机柜上,机柜从地板一直延伸到天花板,排成一行又一行,像是一座用钢铁和玻璃建造的城市。整个房间有167平方米,但你感觉它比一座教堂还要宏伟——不,还要压抑。因为噪音。

噪音是第一个让你意识到自己正站在未来的东西。

那不是嗡嗡声,不是轰鸣声,而是一种你从未听过的声音:一万八千个真空管同时运转时产生的电子噪声,混合着冷却风扇的呼啸,混合着继电器咔哒咔哒的开关声。它像是一头活着的巨兽在呼吸。你不得不扯着嗓子跟身边的人说话,但即使这样,你也常常听不清对方在说什么。

这台机器叫ENIAC——电子数值积分计算机(Electronic Numerical Integrator and Computer)。它花了将近五十万美元(相当于今天的八百多万),重达三十吨,占地一千五百平方英尺。它是人类历史上第一台通用电子计算机,是那个时代最复杂的机器,没有之一。

但此刻,你注意到了一个奇怪的事情。

这台造价几百万的庞然大物——这个占据了整个房间的钢铁巨兽——没有键盘

没有屏幕。没有鼠标。没有任何你印象中"电脑"应该有的东西。

那怎么让它工作呢?

你走近一个机柜,看到了答案:一排排开关和插线板

那些开关,每一个都只有两个位置:开,或者关。那些插线板,看起来像老式电话交换机上的插孔,密密麻麻地排列着,每一个都需要一根线缆来连接。整台机器上有大约六千个这样的开关,以及数不清的插线板接口。

现在,有人让你用这台机器计算"1+1"。

你盯着那些开关和插线板,脑子里一片空白。

你不知道该拨哪个开关。不知道该把哪根线插到哪个孔里。你甚至不知道"1"在这台机器上是怎么表示的——是用一个真空管的亮灭来表示?还是用一组开关的开合来表示?你需要拨多少个开关才能表示一个数字?如果需要多根线,它们应该按什么顺序连接?

这就是1940年代的现实。要让一台计算机做任何事情——哪怕只是计算一加一等于二——你需要** physically**重新配置它的硬件。你需要理解每一个真空管的功能,每一条线路的走向,每一个开关的逻辑含义。你需要像一个钟表匠一样,用双手把这台机器的内部结构重新拼装一遍,只为了完成一个算术运算。

而更疯狂的是:当你终于把所有的开关拨好、所有的线缆插好,按下"启动"按钮,机器开始运转——真空管们疯狂地闪烁着,继电器噼里啪啦地跳动着——你算完了"1+1=2",然后你想算下一个问题,比如"2+3"。

你需要把所有开关重新拨一遍,把所有线缆重新插一遍

每一次计算,都是一次物理上的重新接线。每一个新问题,都意味着你要把这台三十吨的巨兽从里到外重新"组装"一次。

这就是人类与机器对话的最初方式。

不是打字。不是编程。是插拔电线

你也许会想:这算什么"计算"?这分明是体力劳动。但请你想一想——那些在1940年代站在ENIAC面前的人,他们面对的不是一个简单的体力问题。他们面对的是一个人类从未遇到过的认知挑战:如何把人类的思维,翻译成一台只认识"开"和"关"的机器能够理解的指令?

这个问题,将定义此后八十年的技术史。

而回答这个问题的第一批人,不是你可能想象的那些穿着白大褂的男性科学家。

是六位女性。


二、被遗忘的"计算机"

1945年的费城,战争还没有完全结束。

在摩尔学院的一间小办公室里,六个年轻女性坐在一张桌子旁边,面前摊开着一堆蓝图。不是电路图——虽然她们确实需要看懂电路图——而是ENIAC的逻辑结构图,上面画着这台机器的每一个功能单元、每一条数据通路、每一个时序信号。

她们是Jean JenningsBetty SnyderMarlyn MeltzerRuth LichtermanFrances BilasKay McNulty

她们的头衔是"计算机"(Computer)。

请注意,这不是一个比喻。在1940年代,"计算机"这个词首先指的是一种职业——一种由人来充当的职业。在电子计算机发明之前,所谓的"computer"就是那些坐在桌前、用纸和笔做数学计算的人。而在那个年代,这种工作被认为适合女性——就像"电话接线员"一样,细致、耐心、不需要太高的"创造力"。

这六位女性大多拥有数学学位。她们被招募到摩尔学院,最初的工作就是用手摇计算器做弹道计算——为陆军算炮弹的飞行轨迹。这是一份枯燥的、无穷无尽的计算工作,但她们做得很好。

然后,ENIAC来了。

当这台机器在1945年底基本建造完成时,军方和摩尔学院的工程师们面临一个尴尬的问题:机器造好了,但没有人知道怎么让它运行

工程师们设计了硬件——他们知道每一个真空管怎么焊、每一条线路怎么走。但让这台机器"做计算"?那需要一种全新的技能,一种没有人教过、也没有教科书可查的技能。工程师们觉得这不是他们的事——他们是硬件设计师,不是"操作员"。

于是,这个任务被扔给了那六个会算弹道的女性。

"你们去搞定它。"

没有文档。没有手册。没有先例。

Jean Jennings后来回忆说,她们拿到的只有一叠ENIAC的蓝图——那些蓝图上画着机器的物理结构,每一个面板、每一个开关、每一个插线板的位置。她们需要自己弄明白:这些硬件是怎么工作的?怎样才能让它们执行一个数学计算?

这不是"编程"——那个词当时根本不存在。这是某种更原始、更直觉的东西。她们需要在大脑中建立起一台三十吨重、一万八千个真空管的机器的完整心智模型。她们需要理解:如果我拨动这个开关,那根线上的电平就会改变,那个真空管就会导通,那个继电器就会吸合,然后数据就会沿着这条路径流动……

她们必须像机器一样思考

Betty Snyder是六个人中最有天赋的一个。她的同事后来说,Betty有一种不可思议的能力——她可以在脑子里"走"完ENIAC的整个执行流程,一步一步地追踪数据在机器内部的流动,就像你在脑子里想象一个弹球在弹珠台上弹跳的轨迹一样。她不需要真的去拨开关、看结果,她就能在脑子里"运行"这台机器。

这种能力,在今天叫做调试(debugging)。但在1945年,连这个词都还没有被发明。

这六位女性花了数周时间,摸索出了让ENIAC工作的方法。她们发明了一种工作流程:先在纸上把计算过程分解成一系列步骤(后来的人把这叫做"算法"),然后把每个步骤映射到ENIAC的具体硬件单元上(后来的人把这叫做"资源分配"),最后把每一步对应的开关设置和线缆连接记录下来(后来的人把这叫做"程序")。

她们没有发明编程语言。她们做的事情比发明编程语言更根本——她们发明了**"让机器按照人的意图工作"这个概念本身**。

1946年2月14日,ENIAC向公众展示。演示非常成功,机器在几秒钟内算出了炮弹轨迹——而人工计算需要几个小时。媒体沸腾了。报纸称之为"巨脑"。

但报纸上没有提到那六位女性的名字。

在官方的记录里,她们只是"操作员"。不是发明者,不是工程师,不是科学家。只是按按钮的人。

在此后的几十年里,计算机科学蓬勃发展,编程语言一个接一个地被发明,图灵奖一届一届地颁发,但Jean、Betty、Marlyn、Ruth、Frances和Kay的名字几乎从未被提起。她们回到了各自的家庭,做了母亲、妻子、社区志愿者。她们中的大多数人再也没有接触过计算机。

直到2014年——ENIAC公开展示的68年后——这六位女性才被追认进入互联网名人堂(Internet Hall of Fame)和女性科技名人堂(WITI Hall of Fame)。

68年。

在这68年里,编程语言已经从"插拔电线"进化到了人类可以坐在笔记本电脑前、用接近英语的语法写出复杂的程序。从机器语言到汇编语言,从Fortran到C,从Java到Python,从C++到Rust——每一次进化,都是为了让"人指挥机器"这件事变得更容易一点。

但最初的那一步——从"什么都没有"到"让机器听懂人的第一个指令"——是由六个被叫做"操作员"的年轻女性完成的。

她们没有留下代码。她们留下的东西比代码更珍贵:一个证明——人类可以教会机器思考。

而她们使用的方法——用双手物理地重新配置硬件——很快就被证明是不可持续的。

一个根本性的问题浮出水面:有没有一种方法,可以不碰硬件就让机器做不同的事?

这个问题的答案,将开启一段长达八十年的攀登。


三、抽象的阶梯

让我给你讲一个故事。

假设你是一个建筑师,你要盖一栋楼。你有两种选择:

第一种:你自己烧砖、自己和水泥、自己切割钢筋、自己铺每一根电线、自己拧每一颗螺丝。从原材料开始,一切亲力亲为。

第二种:你画图纸。然后砖厂给你送砖,水泥厂给你送水泥,钢结构厂给你送预制件,电气公司帮你布线。你只需要关注设计——这栋楼长什么样、有多少层、每层干什么用——而不用关心砖是怎么烧出来的。

第一种方法,你盖一间平房就要累死。第二种方法,你可以盖一座摩天大楼。

区别在哪里?抽象

第二种方法之所以可行,是因为你不需要知道砖的分子结构,不需要知道水泥的化学配方,不需要知道电线的铜是怎么冶炼的。你只需要知道:这块砖能承受多大的压力,这根电线能安全通过多大的电流。你把所有底层的复杂性封装起来了,只在更高的层面上做决策。

这就是编程语言史的核心逻辑——抽象的阶梯

想象一座梯子。梯子的最底层是硬件——真空管、晶体管、逻辑门、电路。这是最"真实"的层面,是电子真正流动的地方。在这里,一切只有"0"和"1",只有电压的高和低,只有开关的开和关。

梯子的第二层是机器语言。每一个二进制指令——比如"把寄存器A里的数加到寄存器B里"——都直接对应硬件的一条物理操作。这是机器唯一能"听懂"的语言。但人类几乎不可能直接用机器语言写出一个像样的程序——就像你不可能用"开、关、关、开、开、关……"这样的二进制序列来写一封情书。

梯子的第三层是汇编语言。它做的事情很简单:给每一个机器指令起一个人类可读的名字。比如,不用写"1011000001100001",而是写"MOV AL, 61h"。这就像给每一块砖贴上标签——砖还是那块砖,但你至少能叫出它的名字了。

再往上是高级语言。Fortran、COBOL、C、Java、Python……每一层都比上一层更远离硬件,更接近人类的思维方式。你可以写x = x + 1,而不用管这个"加1"在硬件层面到底意味着哪些晶体管翻转了、哪些数据在总线上移动了、哪些内存地址被读写了。

每往上爬一层,人就轻松一点,但机器就"吃力"一点。

因为机器只懂最底层——只懂0和1。你在高级语言里写一句print("Hello, World!"),机器需要经历一个漫长的"翻译"过程:高级语言→汇编语言→机器语言→电信号→真空管(或晶体管)的开关动作。这个翻译过程需要时间和资源。

这就是编程语言史上最核心的张力:抽象 vs 效率

你想让编程更容易(更高的抽象),但代价是程序运行得更慢(更多的翻译开销)。你想让程序跑得更快(更低的抽象),但代价是程序员要操更多的心、写更多的代码。

这就像盖房子:你可以用预制件快速盖起一栋大楼(高抽象、高效率),但如果你追求极致的结构性能,你可能需要手工挑选每一块石材、精确计算每一个受力点(低抽象、高性能)。两种方式都有道理,但你很难兼得。

在本书中,你将一次又一次地看到这个模式重演:

1957年,IBM的John Backus团队发明了Fortran——第一个广泛使用的高级语言。当时的程序员们嘲笑它:"用这种高级语言写出来的程序,运行速度比手写机器码慢20%!太浪费了!"但Fortran让编程效率提高了十倍。人们很快发现:程序员的时间比机器的时间贵得多。

1972年,Dennis Ritchie发明了C语言。它比Fortran更灵活,但比汇编语言更高级。C语言的成功在于它找到了一个完美的平衡点——足够抽象让程序员舒服,又足够接近硬件让程序跑得快。它成了此后三十年编程语言的"通用语"。

1995年,Sun公司推出了Java。它的口号是"一次编写,到处运行"。代价呢?Java程序运行在一个虚拟机上——就像你在中文和英文之间安排了一个同声传译,虽然方便了你跟不同国家的人交流,但每次对话都多了一层翻译的延迟。Java的拥趸说:"没关系,硬件越来越快,这点延迟不算什么。"反对者说:"这是对计算资源的犯罪!"

2000年代,Python逐渐流行。它的哲学是"人生苦短,我用Python"——用尽可能少的代码完成尽可能多的事。代价是运行速度?Python的粉丝耸耸肩:"反正大部分时间你的程序在等网络、等数据库、等用户输入,CPU那点速度差异根本感觉不到。"

你看出规律了吗?

每一次抽象的跃升,都伴随着一场争吵。

支持者说:"这是进步!我们终于不用关心那些底层细节了!"

反对者说:"这是堕落!你们在浪费机器的性能!你们甚至不理解自己的程序在底层到底做了什么!"

然后,时间证明了一切。硬件的进步(摩尔定律)通常会消化掉抽象带来的性能损失。今天,你用Python写一个程序,它的运行速度可能比1990年用C写的同类程序还快——因为今天的CPU比那时候快了一千倍,足以弥补Python的"翻译开销"。

但这场争吵永远不会结束。因为每一代新语言,都在推动抽象的边界再往上一步——而每一步,都有人说"太远了"。

这就是抽象的阶梯:一架没有顶端的梯子。每一代程序员都站在前人的肩膀上,往上爬一步。每一步都让人类离硬件更远一点,离"用人类的方式思考"更近一点。

而这本书,讲的就是这架梯子上的每一个关键台阶——以及站在每一个台阶上的那些人。

但在我们开始攀登之前,让我先给你讲一个更古老的故事。

一个关于语言的故事。


四、通天塔的诅咒

《创世记》第十一章,有这样一段记载:

那时,天下人的口音言语都是一样。他们往东边迁移的时候,在示拿地遇见一片平原,就住在那里。他们彼此说:"来吧,我们要建造一座城和一座塔,塔顶通天,为要传扬我们的名,免得我们分散在全地上。"

你大概已经猜到了——这就是巴比伦塔(Tower of Babel)的故事。

在传说中,大洪水之后,人类聚集在美索不达米亚平原上。他们只有一个民族,一种语言,一个目标。他们决定建造一座通天之塔——不是为了敬神,而是为了证明自己。"塔顶通天",他们要到达上帝的领地,"传扬我们的名"。

上帝看到了这一切。他说了一句意味深长的话:

"看哪,他们成为一样的人民,都是一样的言语,如今既做起这事来,以后他们所要做的事就没有不成就的了。"

注意这句话的逻辑:**语言的统一是一切成就的前提。**当所有人都说同一种语言时,没有沟通障碍,没有误解,没有信息损耗——人类可以协调无限大的工程,可以完成任何目标。这是一种可怕的力量。

于是上帝做了一件事:变乱了他们的语言

"变乱"——希伯来原文是balal,意思是"混淆"、"搅乱"。一瞬间,正在搬砖的工人突然听不懂工头的指令了。正在设计拱门的建筑师无法跟石匠沟通了。正在计算承重结构的工程师发现没人能理解他的公式了。

工程崩溃了。

不是因为技术不行。不是因为材料不够。不是因为人手不足。

是因为语言不通。

当人们无法精确地表达自己的意图、无法准确地理解他人的指令时,再伟大的工程也无法继续。巴比伦塔的倒塌,不是因为上帝的雷霆——而是因为沟通的断裂

现在,请你想一想编程语言的历史。

你会发现,这个故事有一个绝妙的反转

在巴比伦塔的传说中,人类的问题是他们只有一种语言——而这种统一的语言让他们强大到让上帝感到威胁。

但在编程的世界里,情况恰好相反:人类想让机器听懂人话,但"人话"本身就有无数种。

英语程序员想用英语的语法写程序。法语程序员想用法语的语法。数学家想用数学符号。商人想用商业术语。科学家想用方程式。军人想用指令式的命令。哲学家想用逻辑推理。

每一个群体都觉得自己的思维方式才是"最自然的",都应该成为人与机器沟通的"标准语言"。

于是,从1950年代到今天,人类发明了超过九千种编程语言。

九千种。

比地球上现存的活跃自然语言(大约七千种)还多。

Fortran、COBOL、Lisp、ALGOL、BASIC、Pascal、C、C++、Objective-C、Java、C#、Python、Ruby、PHP、JavaScript、Go、Rust、Swift、Kotlin、TypeScript……这还只是那些你听说过的。在历史的角落里,还躺着成千上百种已经被遗忘的语言——它们曾经被寄予厚望,曾经有人为它们写过教科书,曾经有人在会议上为它们激烈辩护,然后它们死了。

为什么会有这么多?

因为每一种编程语言,都是某个人对"人应该如何与机器沟通"这个问题的一个回答

而每个人——每个时代、每个文化、每个应用领域——对这个问题的回答都不一样。

有人觉得程序应该像数学证明一样严谨(Haskell)。有人觉得程序应该像日常对话一样随意(Python)。有人觉得程序应该像军事命令一样精确(Ada)。有人觉得程序应该像搭积木一样灵活(Scratch)。

这就是编程语言的"巴比伦塔困境":我们想建一座通天之塔——一个统一的、完美的、所有人都能用来跟机器对话的语言——但我们的"语言"本身就是分裂的。

更讽刺的是,在《创世记》的故事里,上帝用语言的混乱来阻止人类建塔。而在编程的历史里,语言的多样性反而成了创新的源泉。Fortran为科学计算而生,COBOL为商业数据处理而生,Lisp为人工智能研究而生,C为操作系统开发而生——每一种语言都因为它的"偏见"而变得强大。

但代价呢?

代价是巴别式的混乱

今天,一个大型软件项目可能需要同时使用五种编程语言。一个程序员职业生涯中可能需要学习十种语言。不同语言写成的程序之间互相通信,需要复杂的"翻译层"——就像联合国大会上的同声传译系统一样,昂贵、脆弱、容易出错。

我们被困在了巴比伦塔的废墟中。

但也许——也许——这正是这个故事最迷人的地方。

因为在这九千种语言的混战中,有一些模式开始浮现。有一些教训被反复学到。有一些天才,在混乱中看到了秩序,在分裂中找到了桥梁。

他们不是在建一座塔。他们是在铺设一条路——一条从"机器的语言"通向"人的语言"的道路。

这条路,从1940年代的插线板开始,一直延伸到今天的AI辅助编程。它走了八十年。而在这条路上,每一个关键的转折点,都有一个(或一群)具体的人——有他们的执念、他们的偏见、他们的灵光一闪、他们的致命失误。

这本书要讲的,就是这些人的故事。


尾声:飞蛾

让我把时间拨到1947年9月9日。

地点:哈佛大学艾肯计算实验室。

人物:Grace Hopper,美国海军预备役少校,数学博士,哈佛大学Mark II计算机项目的核心成员。

这一天,Mark II出了故障。

这台机电式计算机突然停止了运算——不是那种缓慢的、可预期的停机,而是一种让人抓狂的、间歇性的死机。它运行一会儿,停下来。再运行一会儿,又停下来。工程师们检查了所有的继电器,测试了所有的线路,一无所获。

最后,有人打开了第70号继电器的外壳。

在里面,他们发现了一只飞蛾

一只真正的、物理意义上的飞蛾。它不知道什么时候飞进了机器,被夹在了继电器的触点之间,导致触点无法正常闭合。

工程师们小心翼翼地把飞蛾取出来,用胶带把它粘在了工作日志上。在旁边,他们写了一行字:

"First actual case of bug being found."

(发现了第一个真正的"虫子"案例。)

Grace Hopper后来把这个故事讲了一遍又一遍,"debugging"这个词也因此广为流传——虽然她本人总是谦虚地说,这个词在她之前就已经被工程师们使用了。

但这不是这个故事的重点。

重点是:在捉飞蛾的同时,Grace Hopper正在酝酿一个疯狂的想法

一个在当时几乎所有计算机专家都认为荒谬绝伦的想法。

一个将彻底改变人类与机器关系、开启整个编程语言历史的想法。

她的想法是:我们能不能用接近英语的语法来写程序?

想象一下1947年说这句话的效果。这就好比在今天说:"我们能不能用意念来控制宇宙飞船?"

当时的计算机只认识数字——0和1。你让一台只认识数字的机器去"理解"英语?这就像让一条鱼去理解微积分——不是难不难的问题,而是范畴错误

但Grace Hopper不这么认为。

她相信——几乎是信仰般地相信——人类不应该去适应机器的语言。机器应该来适应人类的语言。

"我们不需要训练人类像机器一样思考,"她说。"我们需要训练机器去理解人类的思考。"

这个想法,在当时看来,比建造巴比伦塔还要狂妄。

但Grace Hopper做到了。

她发明的东西,将开启编程语言历史上第一个真正的革命。

而这个故事——真正的故事——从下一章开始。

第一章:当机器学会说英语——Grace Hopper与编译器的诞生。

第一章:黑夜中的炬火

"人类在语言上最大的成就,不是创造了词汇,而是发明了比喻。"

—— 理查德·费曼


一、机器码的黑暗时代

1949年,一个深秋的凌晨,宾夕法尼亚大学摩尔电气工程学院的地下室里,28岁的研究生罗伯特·埃弗雷特正对着一卷纸带发呆。

纸带长约三十米,上面密密麻麻地打满了小孔——每一行八个孔位,有些位置打了孔,有些没有。在普通人眼里,这不过是一条被虫蛀得千疮百孔的牛皮纸。但在罗伯特眼里,这是他整整三天的生命。

三天。七十多个小时。两千多个二进制数字。

他小心翼翼地把纸带装进ENIAC的读带器,按下启动键。机器嗡嗡作响,真空管一盏接一盏地亮起来,像一排被唤醒的萤火虫。然后——

什么也没发生。

不,不对。发生了。机器的指示灯以一种完全错误的方式闪烁,打印纸上吐出了一串毫无意义的数字。罗伯特的胃猛地一沉。他知道这意味着什么:纸带上某个地方,有一个孔打错了。也许只是第1847行的一个"1"被打成了"0"。但就是这一个孔的偏差,足以让整台造价五十万美元的机器变成一个昂贵的废物。

他得从头检查。

这就是机器码时代的日常。

让我带你回到那个年代。1940年代末到1950年代初,所谓的"编程",是一件今天的人完全无法想象的事情。那时候没有键盘,没有屏幕,没有IDE,没有语法高亮,甚至没有一个像样的文本编辑器。你要告诉计算机做事,唯一的办法就是在纸带上打孔——每一个孔代表一个二进制位(bit),要么有孔(1),要么没孔(0)。

计算机的CPU只认识一种语言:机器码。它是一串纯粹的数字,每一个数字对应CPU内部一个极其具体的物理动作——把寄存器A的内容移到寄存器B,或者把内存地址00101100里的数字和寄存器C里的数字相加。没有名字,没有含义,只有数字。

想象一下:你现在要写一封情书,但你不能写任何文字。你只能用摩尔斯电码——不,比摩尔斯电码更残酷——你得用一种只有收信人才能理解的、完全私密的密码。而这封情书有三千行。

更要命的是,每台计算机的机器码都不一样

ENIAC有ENIAC的机器码,EDVAC有EDVAC的机器码,IBM 701有IBM 701的机器码。它们之间没有任何兼容性。这就好像——你花了三个月学会了一门语言,兴冲冲地跑到邻国去,发现那里的人说的是一种你完全听不懂的语言。不是方言差异,是连字母表都完全不同。

一个程序员如果从一家公司跳到另一家公司,他之前积累的所有编程经验,几乎等于清零。他得重新学一套全新的0和1的组合规则,就像一个人移民到另一个星球。

那时候的程序员,与其说是"软件工程师",不如说是"纸带工匠"。他们每天的工作就是坐在一盏台灯下,用一把特制的打孔器,在长长的纸带上一格一格地戳孔。手不能抖,眼不能花,脑子必须同时维持着三层抽象——最上面是数学逻辑,中间是算法流程,最底下是这台机器独有的指令编码表。

一个错误,可以毁掉一周的工作。

而没有人觉得这有什么不对。在那个年代,计算机本身就是个奇迹。能让它动起来就已经谢天谢地了,谁还敢奢求"方便"?


二、格蕾丝·霍珀与"编译器"的疯狂想法

1947年9月9日,哈佛大学马克二号计算机的实验室里,发生了一件小事。

这台计算机是军方用来做弹道计算的庞然大物,占地半个房间,内部密布着一万七千多个继电器——一种用电磁铁控制开关的机械装置。那天上午,机器突然停机了。工程师们排查了半天,最后打开了第70号继电器的外壳,发现了一只飞蛾。

一只真正的、活生生的飞蛾,被夹在了继电器的触点之间,导致电路无法闭合。

工程师格蕾丝·霍珀(Grace Hopper)走过来,看了一眼那只挣扎的飞蛾,从口袋里掏出一卷胶带,把飞蛾粘在了工作日志上。她在旁边写了一行字:

"First actual case of bug being found."(发现了第一个真正的"虫子"案例。)

然后她把日志合上,继续工作。

这个画面后来成了计算机科学史上最著名的传说之一。那只飞蛾的标本,连同那页日志,至今保存在美国国家历史博物馆。虽然"bug"这个词在爱迪生时代就已经被工程师用来形容故障了,但霍珀的这只飞蛾,让这个词永远和计算机绑在了一起。

不过,霍珀真正的贡献,远比抓一只飞蛾要震撼得多。

格蕾丝·布鲁斯特·穆雷,1906年生于纽约。她从小就对机械着迷,七岁时为了弄懂闹钟是怎么工作的,把家里的七座钟全部拆了个遍——然后成功地把它们全部装了回去。这个细节很重要,因为它预示了她一生的思维方式:如果我能拆开它,我就能理解它;如果我理解它,我就能改造它。

她本科去了瓦萨学院,数学和物理双修。后来拿到耶鲁大学的数学博士学位——在那个年代,女性拿到耶鲁博士学位,本身就是一种宣言。二战爆发后,她做了一个让所有人吃惊的决定:加入美国海军预备队。她的家人反对,朋友不解,但她还是去了。理由很简单:"国家需要我。"

战争结束后,霍珀被分配到哈佛大学计算实验室,在马克系列计算机上工作。正是在这里,她第一次直面了机器码的痛苦。

她后来说过一句话,语气平淡,但信息量巨大:"我们当时花在做编码上的时间,比花在解决实际问题上的时间还多。"

这句话像一颗种子,在她脑子里生根发芽。

1949年,霍珀加入了一个雄心勃勃的项目——制造一台能存储程序的电子计算机,叫UNIVAC(通用自动计算机)。这是美国第一台商用计算机,也是历史上第一台真正意义上的"通用"计算机。

在UNIVAC上工作时,霍珀产生了一个在当时看来近乎疯狂的想法:

为什么不让计算机自己写程序?

她说的不是让计算机"自主思考"——那是科幻小说的事。她的想法更具体、更务实:能不能写一个程序,让它接收人类用接近英语的指令写成的代码,然后自动把这些指令翻译成机器能执行的二进制码?

她把这个程序叫做编译器(compiler)——来自拉丁语 compilare,意思是"收集、汇编"。

你可以这样理解她的想法:假设你是一个只会说中文的人,面前站着一个只会说斯瓦希里语的非洲工匠。你们之间需要一个翻译。霍珀说:我们为什么不让计算机来当这个翻译呢?人类写"中文",计算机翻译成"斯瓦希里语",然后CPU执行。

这个想法在当时有多疯狂?

这么说吧——当时最顶尖的计算机科学家,包括冯·诺依曼本人,都公开表示怀疑。他们的理由听起来也很有道理:

"计算机只能做精确的逻辑运算,英语充满了歧义和模糊性,机器怎么可能理解?"

"编译器生成的代码肯定比人手写的机器码效率低,没有人会接受性能损失。"

"这是在浪费时间。有那个功夫,不如直接学机器码。"

霍珀后来回忆说:"每个人都告诉我这不可能。他们说,计算机不能做商业数据处理,不能理解英语,不能做任何超出纯数学的事。"

她没有争辩。她只是回去继续写代码。

1952年,A-0系统完成了。这是世界上第一个编译器。它并不完美——功能很有限,只能处理简单的数学运算指令——但它证明了霍珀的直觉是对的:人类确实可以用一种更接近自然语言的方式来编写程序,然后让机器自己去理解。

霍珀有一句名言,后来被无数人引用:

"做一件事,比解释为什么做这件事容易得多。"

(It's often easier to ask for forgiveness than to ask for permission.)

她先用行动证明了可能性,然后才用结果说服了那些嘲笑她的人。这大概是所有颠覆性创新者的共同命运——先被当作疯子,再被当作天才。


三、汇编语言——第一步抽象

在霍珀埋头研究编译器的同时,另一条平行的进化路线也在悄悄展开。

这就是汇编语言(Assembly Language)。

如果说机器码是"直接对CPU喊二进制数字",那么汇编语言就是"用简写符号跟CPU说话"。它并没有改变你跟CPU对话的本质——每一条汇编指令,都严格对应着一条机器码指令——但它做了一件看似微小、实则革命性的事情:给那些冰冷的数字起了名字

举个例子。在某个型号的机器上,"把两个数相加"这个操作对应的机器码可能是 10110000。没有人能一眼看出这串数字是什么意思。但在汇编语言里,同样的操作被写成了:

  
ADD
  

三个字母。一目了然。

再比如,"把数据从内存搬到寄存器"这个操作,机器码可能是 01011001 00000010,汇编语言里则写作:

  
MOV AX, [0200]
  

意思是:把内存地址0200处的数据,移动到AX寄存器里。

这看起来只是换了个写法,但它带来的认知解放是巨大的。

让我用一个比喻来说明。想象你是一个飞行员,要操控一架飞机。在"机器码模式"下,你的驾驶舱里没有仪表盘,没有操纵杆,只有一排排开关,每个开关上标着一个数字——"按下00110101,引擎转速增加200转"。你得背下几百个数字编码才能起飞。

而在"汇编模式"下,你有了仪表盘和操纵杆。虽然它们还是直接连着引擎的机械结构,虽然你还是在手动操控,但至少——你能看到高度表了,你能分清油门和方向舵了。

汇编语言就是程序员的第一副仪表盘。

它引入了几个关键概念,这些概念后来成为了所有编程语言的基石:

助记符(mnemonic):用ADD、SUB、MOV这样的缩写代替二进制操作码。

标签(label):给一段代码起个名字,这样跳转的时候不用算地址,直接写 JMP START 就行。

(macro):把一组常用指令打包成一个"快捷方式",以后用一个名字就能调用。

这些概念今天看来理所当然,但在1950年代初,它们每一个都是了不起的抽象。所谓抽象,就是隐藏细节、暴露本质。你不需要知道ADD指令在CPU内部到底是怎么通过电路实现的,你只需要知道"ADD就是加法"。

但汇编语言有一个根本性的局限:它仍然和具体的硬件绑定

一台IBM 704上的汇编代码,拿到IBM 709上就跑不了。虽然比纯机器码好了一些(至少你不用背数字了),但本质上你还是在跟特定型号的CPU一对一对话。换一台机器,你的助记符可能完全不同——这台机器叫ADD,那台机器可能叫ADP。

换句话说,汇编语言解决了"可读性"的问题,但没有解决"可移植性"的问题。它是一座桥,但这座桥太短了——只够从"纯数字"走到"简写符号",还远远到不了"人类自然语言"的彼岸。

那座更长的桥,需要另一个人来建造。


四、FORTRAN——真正的破局者

1953年的一个下午,纽约麦迪逊大道590号,IBM总部大楼。

一个年轻人推开了部门主管的门。他叫约翰·巴科斯(John Backus),二十六岁,瘦高个,戴着眼镜,说话带着费城口音。他三年前加入IBM,原因是——说来有些讽刺——他大学数学学得不好,被教授劝退后, IBM的程序员培训班成了他的退路。

但就是这个"数学不好"的年轻人,在IBM做计算工作的三年里,发现了一个让他寝食难安的问题。

那时候IBM最新的大型计算机是IBM 704,主要客户是各大实验室和大学的科学家。这些科学家——物理学家、化学家、航空工程师——个个都是聪明绝顶的人,但他们中的大多数对编程深恶痛绝。原因很简单:他们得用汇编语言把数学公式一个运算一个运算地翻译成机器指令。一个求解微分方程的科学家,本来应该在思考物理问题,结果把80%的时间花在了跟机器码搏斗上。

巴科斯找到他的老板,说了一句在当时堪称大胆的话:

"如果我们能设计一种语言,让科学家直接用接近数学公式的方式来写程序,然后让计算机自动把它翻译成机器码——那编程的时间能缩短一半。"

他的老板,一个务实的IBM高管,盯着他看了几秒钟。

"给你五个人,"老板说,"还有一小笔预算。但你只有一年时间。如果做不出来,这个项目就取消。"

巴科斯带着他的四人团队(后来增加到十几个人),在IBM总部顶层的一间小办公室里开始了工作。他们给这个项目起了一个名字:FORTRAN——Formula Translation的缩写,意思是"公式翻译"。

他们的目标很明确:设计一种语言,让科学家可以像写数学公式一样写程序。比如,科学家想计算 C=A+BC = A + B,在FORTRAN里就写:

  
C = A + B
  

就这么简单。没有寄存器编号,没有内存地址,没有二进制操作码。你写什么,计算机就理解什么——至少看起来是这样。

当然,"看起来"这三个字背后,藏着巨大的工程挑战。FORTRAN编译器需要做到:

词法分析:把源代码拆分成一个个有意义的"词"(token)。

语法分析:理解这些词之间的关系,构建一棵语法树。

优化:把高级语言翻译成机器码时,要尽可能生成高效的代码——因为当时所有人都断言"编译器生成的代码不可能比人手写的好"。

巴科斯团队发明了一种叫做递归下降分析的技术来解决语法分析问题,这项技术至今仍是编译器设计的基础方法之一。他们还设计了寄存器分配算法,让编译器能智能地决定哪些变量该放在哪个寄存器里——这在当时是一个全新的研究领域。

1957年4月,FORTRAN I正式发布。

它被装在一盒纸带里——讽刺的是,FORTRAN的编译器本身,最初也是用汇编语言写的。编译器团队花了整整两年半的时间,在IBM 704上手工编写了大约两万五千行汇编代码,才造出了这个能把"人话"翻译成"机器话"的程序。

发布的那天,巴科斯和他的团队并没有庆祝。因为他们知道,真正的战斗才刚刚开始。

业界的声音几乎是清一色的嘲讽:

"编译器生成的代码肯定比手写机器码慢。真正需要性能的人不会用它。"

"科学家不会愿意学一门新语言的。"

"这不过是又一个玩具。"

《商业周刊》的一篇报道甚至直言不讳地说:"FORTRAN注定失败。"


五、反转——可读性战胜极致性能

嘲讽者们的预言,对了一半。

FORTRAN编译器生成的代码,确实比最优秀的手写机器码要。慢多少呢?大约20%到30%。在那个计算机每秒只能执行几万次运算的年代,这个差距不算小。性能纯粹主义者有理由不满。

但他们忽略了另一面——一个在当时很少有人认真计算过的数字。

一个训练有素的程序员,用汇编语言写一个完整的科学计算程序,需要多长时间?答案是:几个月

同一个程序员,用FORTRAN写同样的程序,需要多长时间?答案是:几天到几周

开发效率提升了十倍甚至更多。

巴科斯后来在一次采访中说了那句后来被刻进计算机科学发展史的话——虽然他说得更含蓄:

"我们的客户告诉我们,他们愿意用20%的性能损失,换取编程时间缩短一半。但实际上,我们给他们的远不止一半。"

这里藏着一个深刻的经济学洞察,它的重要性怎么强调都不过分:

工程师的时间,比计算机的机时更贵。

一台IBM 704的月租金大约是一万美元(1950年代的美元),而一个合格的程序员年薪就有好几千美元。如果一个程序员花三个月用汇编语言写程序,人力成本就远超机器成本了。FORTRAN把三个月压缩到两周——省下来的不是机器时间,是人的时间

而人的时间,永远是最贵的资源。

这个逻辑,在七十年后的今天依然成立。2020年代的云计算时代,我们依然在反复验证巴科斯当年的发现:Python比C慢十倍又怎样?开发速度快五倍就够了。JavaScript的性能不如Rust又怎样?生态系统和开发体验才是王道。

可读性战胜极致性能——这不是妥协,这是进化。

FORTRAN的成功是爆炸性的。发布后不到一年,IBM收到了超过两千份来自客户的FORTRAN程序。到1958年,IBM 704上超过一半的程序是用FORTRAN写的。那些曾经嘲笑它的人,悄悄地开始学了。

更惊人的是FORTRAN的生命力。

FORTRAN并没有停留在FORTRAN I。它不断进化:FORTRAN II(1958)增加了子程序,FORTRAN IV(1962)增加了更多的数据类型,FORTRAN 77(1978)标准化了字符串处理,FORTRAN 90(1991)增加了数组操作和模块化编程……

而最让人惊叹的是——直到2020年代,全球排名前列的超级计算机上,仍然运行着大量的FORTRAN代码。

是的,你没有看错。FORTRAN,一门诞生于1957年的语言,在将近七十年后,仍然在世界上最强大的计算机上运行。

为什么?因为科学计算领域有一个残酷的现实:那些用FORTRAN写的气候模拟模型、流体力学求解器、核物理仿真程序,经过了数十年的验证和优化,代码量动辄数百万行。没有人敢轻易替换它们。这些代码就像埋在地基里的钢筋——你看不见它们,但整栋大楼都靠它们撑着。

2020年,FORTRAN迎来了它的第63个生日。它没有死。它只是老了,但依然在工作。


六、尾声与伏笔

让我们暂停一下,回望这段旅程。

从1949年罗伯特·埃弗雷特在地下室里对着一卷纸带发呆,到1957年FORTRAN正式发布,前后不过八年。但这八年里,人类完成了一次认知上的飞跃:我们终于可以用接近人话的方式,跟机器说话了。

机器码是黑夜。汇编语言是第一根火柴。而FORTRAN,是真正的炬火——它照亮了前方漫长的道路,让后来者知道:编程语言这条路,走得通。

格蕾丝·霍珀证明了编译器是可能的。约翰·巴科斯证明了编译器是实用的。他们两个人,一个在理论上破冰,一个在工程上落地,共同打开了编程语言的大门。

但故事远没有结束。

FORTRAN的成功像一块巨石投入湖面,激起的涟漪迅速扩散。整个1950年代末到1960年代初,编程语言如雨后春笋般冒出来——每种语言都有自己的设计哲学,自己的拥护者,自己的"宗教"。

有人觉得FORTRAN太"数学"了,不够通用。有人说汇编语言才是正道,高级语言都是花架子。还有一个人,站在完全不同的维度上,提出了一个更激进的问题:

编程语言到底应该模仿数学,还是模仿逻辑?它应该是一种计算工具,还是一种思维方式?

这个问题,将引爆计算机科学发展史上最深刻的一场哲学战争。

而这场战争的两个主角,一个是来自MIT的约翰·麦卡锡和他的LISP——一门基于数学λ演算的、优雅到近乎哲学的语言;另一个是来自欧洲的彼得·诺尔和尼克劳斯·维尔特,他们继承了ALGOL的衣钵——一门试图定义"编程语言应该长什么样"的、影响深远的语言。

LISP与ALGOL,函数式与命令式,数学之美与工程之美——这是编程语言世界的"冷战"。

但那是下一章的故事了。


彩蛋:格蕾丝·霍珀后来晋升为美国海军准将(Rear Admiral),是当时美军中最年长的现役军官之一。她一生获得了无数荣誉,包括美国国家技术奖章。1986年,79岁的她正式退役。而她当年粘在日志上的那只飞蛾,连同那页写着"First actual case of bug being found"的日志,被永久保存在华盛顿特区的美国国家历史博物馆里。每次有程序员抱怨自己的代码有bug时,不妨想想那只1947年的飞蛾——至少,你的bug不用从继电器里抠出来。


下一章预告:《第二章 哲学战争——LISP与ALGOL的宿命对决》

第二章:黑客与嬉皮士(LISP 与 ALGOL 的哲学战争)

"人们认为计算机科学的困难之处在于计算机太快了。其实恰恰相反——计算机太慢了,而人太笨了。"

—— 艾兹赫尔·戴克斯特拉(Edsger W. Dijkstra)


一、1958年的岔路口

想象一下1958年的美国。

艾森豪威尔坐在白宫里,苏联人刚刚把斯普特尼克一号扔上了天,整个西方世界笼罩在一种微妙的恐慌之中。但在麻省理工学院那栋灰砖大楼的三层,一个更安静、更深刻的革命正在酝酿——不是关于火箭,而是关于人类如何与机器对话

约翰·麦卡锡(John McCarthy)坐在他的办公桌前,桌上堆满了打孔纸带。窗外是查尔斯河灰绿色的冬水,暖气管发出低沉的嗡嗡声。他手里攥着一支铅笔,在一沓草稿纸上反复画着一种奇怪的符号——全是括号,密密麻麻的括号,像一群蜷缩的蝌蚪。

与此同时,在大西洋的另一边,一群欧洲学者正围坐在阿姆斯特丹一间烟雾缭绕的会议室里,用德语、法语和英语三种语言激烈争论着一个看似微不足道的问题:一条语句应该在哪里结束?

这两拨人,一个在波士顿,一个在阿姆斯特丹,彼此相隔六千公里,却在同一年各自交出了一份答卷。这两份答卷,将定义此后六十年编程语言的走向。

FORTRAN在1957年的成功,就像往一潭死水里扔了一块巨石。涟漪迅速扩散——几乎每个月都有新的编程语言冒出来,每个都声称自己比上一个更好。IBM的FORTRAN是为科学家服务的,它快、它实用、它能跑数值计算。但人们很快发现,科学计算只是计算机能做的事情中的一小部分。如果你要处理符号、操纵列表、构建复杂的逻辑结构呢?如果你要写一个能"思考"的程序呢?

FORTRAN说:做不到。

于是,编程语言的世界在1958年前后出现了一个岔路口。一条路通向严谨的数学结构——追求精确、追求规范、追求像欧几里得几何那样无懈可击的形式体系。另一条路通向灵活的表达能力——追求自由、追求抽象、追求让程序员能表达任何可以想象的东西。

打个比方吧。这就像建筑界的分歧:一边是包豪斯的功能主义——"形式追随功能",每一根钢筋都有它的力学理由,每一面墙都承担着结构责任;另一边是后来的解构主义——弗兰克·盖里设计的毕尔巴鄂古根海姆博物馆,钛合金外墙扭曲翻卷,你根本分不清哪里是墙、哪里是顶,但它在阳光下美得令人窒息。

ALGOL是前者。LISP是后者。

而它们之间的战争,比你想象的要激烈得多。


二、ALGOL——欧洲学者的严谨之作

故事要从一个名字说起:Adriaan van Wijngaarden

这个荷兰人长着一张严肃的脸,说话时习惯性地用食指敲击桌面,好像在给每一个音节打拍子。他是阿姆斯特丹数学中心的主任,一个对"秩序"有着近乎偏执追求的人。1956年,当FORTRAN在美国掀起浪潮时,van Wijngaarden却皱起了眉头。

"这不是编程语言,"他对同事们说,"这是一堆拼凑在一起的指令。"

他的批评不是没有道理。FORTRAN的设计充满了实用主义的妥协——变量名只能有六个字符,因为IBM 704的指令字长就那么多;数组下标从1开始,因为工程师们习惯了数学教科书上的写法;甚至语言的语法规则都没有被严格定义,全靠程序员"意会"。这在van Wijngaarden看来,简直就像用胶带和绳子搭建一座桥。

于是,在1957年,他召集了一次会议。地点在苏黎世,参与者来自欧洲和美国的顶尖大学与研究机构。会议的目标只有一个:设计一种"正确的"编程语言

这群欧洲学者——荷兰的van Wijngaarden、瑞士的Niklaus Wirth(一个说话轻声细语但思维极其锋利的年轻人)、丹麦的Peter Naur(他后来因为这段经历而名垂青史)——他们坐在一起,用一种近乎学术辩论的方式,逐字逐句地讨论一种语言应该长什么样。

那场面,与其说是在设计软件工具,不如说是在起草一部宪法。

1958年,他们拿出了第一版:ALGOL 58(Algorithmic Language 1958)。但van Wijngaarden不满意。他觉得这个版本还不够"干净"。于是又经过两年的反复打磨、争吵、妥协,1960年,ALGOL 60诞生了。

那份报告——后世称之为《ALGOL 60报告》——是计算机科学史上最重要的文件之一。它只有薄薄的十几页纸,但每一页都经过了反复推敲。Peter Naur作为主编,把每一个词都当作法律条文来斟酌。

ALGOL 60引入了什么?让我给你列一份清单,你会惊讶于它的影响力:

begin/end块——用一对关键字把一组语句包裹起来,形成一个整体。就像你用方括号把一组食材括起来,告诉厨师"这是一道菜"。这个概念后来变成了几乎所有语言里的"代码块"。

词法作用域——一个变量在哪个范围内有效,由它在代码中的位置决定,而不是由程序运行时的状态决定。想象你在家里叫"爸爸",指的是你爸爸;你在学校叫"老师",指的是你的老师。同一个词,在不同的"范围"里有不同的含义,但规则是清晰的。ALGOL第一次把这个规则写进了语言规范。

递归——一个函数可以调用自己。这听起来很简单,但在当时是一个激进的想法。FORTRAN不允许递归,因为它的内存模型太原始了。ALGOL说:递归是数学思维的核心,一种语言如果不支持递归,就不配叫"算法语言"。

BNF语法描述法——这可能是ALGOL 60最深远的贡献。Backus-Naur Form,用一套简洁的符号来精确描述一种语言的所有合法程序。就像用化学分子式来描述一个化合物的结构——你不需要看实物,光看分子式就知道它长什么样。从此以后,每一种新的编程语言在诞生之前,都要先写出它的BNF语法。

这就是ALGOL的哲学:语言应该像数学论文一样严谨,每一行代码都有明确的语义,每一个符号都有不可歧义的解释。

它做到了。ALGOL 60报告是那样的优美、自洽、无懈可击——就像一座哥特式大教堂,每一根飞扶壁都精确地传递着力学荷载,每一扇玫瑰窗都镶嵌在完美的几何位置上。

但这里有一个问题。

大教堂不能住人。

ALGOL没有输入输出功能。是的,你没看错——这种语言不能从键盘读取数据,也不能在屏幕上打印结果。在ALGOL的设计者看来,I/O(输入输出)是"机器相关"的细节,不够"纯粹",不值得写进语言规范。这就像你造了一辆完美的汽车,有最好的发动机、最精密的变速箱、最优雅的流线型车身——但没有方向盘,也没有轮子。

"纯粹"的代价是"无用"。

ALGOL在欧洲的大学里被广泛教授,成为几代计算机科学家的启蒙语言。但几乎没有人在实际工作中用它来写生产代码。它的后代——Pascal、C、Modula、Ada——统治了工业界,但ALGOL自己,更像是一个被供奉在博物馆里的理念,而非一把被握在手里的工具。

Niklaus Wirth后来回忆说:"我们创造了一种完美的语言,然后发现没有人需要完美。"


三、LISP——麦卡锡的人工智能之梦

现在让我们把镜头转回美国,转回那个在草稿纸上画括号的男人。

约翰·麦卡锡,1927年出生于波士顿,父母是犹太移民。他从小就是一个让老师头疼的学生——聪明得过分,对不感兴趣的事情完全不屑一顾。他在加州理工读本科时,就已经在研究生级别的数学课上旁听了。后来他去了普林斯顿读博士,导师是伟大的拓扑学家Solomon Lefschetz。

但麦卡锡对拓扑学失去了兴趣。让他夜不能寐的是一个更大的问题:机器能思考吗?

1956年夏天,达特茅斯学院。一场后来被载入史册的研讨会在数学楼的一间教室里召开。麦卡锡和他的三位同事——明斯基、罗切斯特、香农——联合发起了一场关于"机器智能"的讨论。在筹备这份提案时,麦卡锡需要给这个新领域起个名字。他不想用"机器思维"——太科幻了;也不想用"自动推理"——太窄了。最后他选了两个字:

人工智能(Artificial Intelligence)。

就这样,一个学科诞生了。

但达特茅斯会议的成果远不如预期。整整一个夏天,与会者们坐在一起聊天、争论、喝啤酒,最后什么实质性的突破都没有。明斯基后来开玩笑说:"那个夏天我们唯一确定的事情是——这个问题比我们想象的难得多。"

麦卡锡没有气馁。他回到MIT后,开始思考一个更基础的问题:如果我要造一个能"思考"的程序,我需要什么语言?

FORTRAN不行。FORTRAN是为数字而生的——它处理整数、浮点数、数组,但让它处理"符号"就力不从心了。而麦卡锡认为,人类思维的核心不是算术,而是符号操作——我们用词语来代表概念,用句子来表达关系,用逻辑来推导结论。

他需要一种能操纵"列表"的语言。不是数字列表,而是任何事物的列表——符号的列表、句子的列表、规则的列表、甚至列表的列表。

1958年,LISP诞生了。名字来自"List Processing"(列表处理)。

LISP的核心思想,用一句话概括:代码和数据是一回事。

这个概念在计算机科学里有一个专门的名字:同像性(homoiconicity)。让我用一个比喻来解释。

想象你是一个厨师。在大多数厨房里(也就是大多数编程语言里),菜谱和食材是两样东西。菜谱写在纸上,食材摆在案板上,你按照菜谱来加工食材。这是FORTRAN的方式——代码是代码,数据是数据,泾渭分明。

但在麦卡锡的厨房里,菜谱本身也是食材。你可以把一份菜谱放在案板上切碎、翻炒、重新组合,变成一份新的菜谱。你也可以在炒菜的过程中临时写一份新菜谱,然后按照它继续做。代码就是数据,数据也可以变成代码——它们是同一种东西。

这就是LISP的魔力。

在LISP里,一切都用括号表示:

  
(add 1 2)        ; 把1和2加起来
  
(define x 10)    ; 让x等于10
  
(if (> x 5)      ; 如果x大于5
  
    (print "大")  ; 就打印"大"
  
    (print "小")) ; 否则打印"小"
  

初看之下,这满屏的括号简直令人抓狂。LISP程序员因此获得了一个外号:"括号数数员"——因为他们每天的工作就是数括号有没有配对。但如果你习惯了这种写法,你会发现它有一种奇特的优雅:所有的东西都是同一种结构——一个左括号、一些元素、一个右括号。没有优先级规则,没有运算符重载,没有语法糖。简单到不能再简单。

如果ALGOL是一把精密的瑞士军刀——每一个工具都有固定的位置和用途,打开来整整齐齐——那么LISP就是一块可以捏成任何形状的橡皮泥。你想让它变成刀?可以。变成勺子?可以。变成一个你从未见过的奇怪工具?也可以。LISP不告诉你该怎么写程序,它只给你最基本的积木——函数列表——然后说:去吧,建造你想建造的东西。

麦卡锡还在LISP中引入了另一个革命性的概念:垃圾回收(Garbage Collection)

在当时的编程世界里,内存是需要程序员自己管理的。你申请一块内存,用完之后必须手动归还。如果你忘了归还,内存就"泄漏"了——就像你借了图书馆的书不还,书架上的空位就越来越少,直到有一天整个图书馆都被借空了。

麦卡锡说:为什么不让计算机自己来做这件事呢?程序创建了一个对象,用完之后不再引用它,系统就自动把它回收,把内存还给可用池。程序员不需要操心。

这就是垃圾回收——一个在1958年看起来像魔法一样的想法。


四、认知崩塌——"自动回收内存"是奢侈的罪

让我带你回到1960年代,感受一下那个时代的"内存经济学"。

1960年,一台IBM 704计算机的价格大约是200万美元(相当于今天的2000万美元)。它的核心内存有多大?大约32KB。是的,你没有看错——32千字节。你手机上随便一张照片的大小,都能把这台价值连城的机器塞满。

在那个年代,内存是按"字"(word)来计算的,每一个字都价值千金。程序员们像守财奴一样精打细算每一个比特——能用16位绝不用32位,能复用内存块绝不申请新的。写程序就像在一张邮票大小的纸上写一封长信——你必须把每一个字都压缩到极致。

然后麦卡锡走过来了,轻描淡写地说:"让机器自己去管理内存吧。"

整个工程界都惊呆了。

这就像一个在沙漠里缺水的人,别人都在一滴一滴地舔舐水壶里的水,而麦卡锡端起水壶往地上倒,说:"别担心,我会找到新的水源。"

自动垃圾回收在1960年代的工程师眼中,是极度奢侈且不负责任的。你凭什么浪费宝贵的内存?你凭什么让计算机花宝贵的CPU周期去做"清理垃圾"这种琐事?你知不知道一毫秒的CPU时间有多珍贵?

LISP程序员因此被其他工程师嘲笑。在MIT的走廊里,FORTRAN和COBOL的程序员们会用一种半开玩笑半认真的语气说:"哦,LISP那帮人啊,就知道喝茶。"意思是他们写一些慢吞吞的、浪费资源的、"不切实际"的程序,就像一群在象牙塔里喝下午茶的英国绅士。

这种嘲笑是有技术根据的。LISP运行在IBM 704上,确实很慢。它需要额外的内存来存储"垃圾回收器"本身需要的元数据,需要额外的CPU时间来追踪哪些对象还在被使用、哪些可以被回收。在同样的硬件上,一个FORTRAN程序可能比等价的LISP程序快十倍甚至二十倍。

但麦卡锡不在乎。

他的回应,如果用一句话来概括,大概是这样的:"你们在优化错误的东西。"

麦卡锡认为,程序员的思维时间比计算机的运行时间更宝贵。如果一种语言能让程序员更快地表达想法、更快地实验、更快地迭代,那么即使它跑得慢一点,也是值得的。尤其是在人工智能研究中——你根本不知道程序最终要做什么,你需要一种能快速尝试、快速修改、快速推翻重来的语言。

这个观点在1960年代是极其超前的。大多数计算机科学家还停留在"机器时间比人贵"的思维模式中——毕竟一台计算机价值几百万美元,而一个程序员的年薪不过几千美元。但麦卡锡看到了未来:硬件会越来越便宜,而人类的理解力不会突然提升。最终,瓶颈不在机器,在人

历史证明他是对的。六十年后的今天,垃圾回收已经成为几乎所有主流语言的标准配置——Java有GC,Python有GC,JavaScript有GC,Go有GC,Rust虽然走了一条不同的路但也在用"所有权"机制解决同样的问题。麦卡锡在1958年的那个"奢侈"想法,如今已经是行业标准。

但在1960年,他只是一个被嘲笑的理想主义者。


五、软件危机——北约会议的哀嚎

时间快进到1968年。

十年间,计算机硬件发生了翻天覆地的变化。集成电路取代了晶体管,计算机变得更小、更快、更便宜。IBM System/360的推出让企业客户们疯狂——终于有了一整套兼容的计算机家族,从低端到高端,软件可以通用。

但一个奇怪的现象出现了:硬件越来越强大,软件却越来越失控。

这不是某一个公司的问题,而是整个行业的通病。大型软件项目一个接一个地失败——不是小失败,是灾难性的失败。预算超支十倍、工期拖延三年、交付的程序根本跑不起来。美国国防部的一个报告说,软件成本已经占到整个计算机系统成本的70%以上,而且这个比例还在上升。

1968年2月,北约科学事务委员会在德国加米施-帕滕基兴(Garmisch-Partenkirchen)召开了一次会议。地点在巴伐利亚阿尔卑斯山脚下的一座酒店里,窗外是白雪皑皑的山峰,室内是一群焦虑到近乎绝望的计算机科学家。

会议的主题只有四个字:软件危机(Software Crisis)。

弗里德里希·鲍尔(Friedrich L. Bauer),慕尼黑大学的教授,这次会议的组织者之一,在开幕致辞中说了一句被后世反复引用的话:

"我们正在用中世纪的生产方式建造大教堂。"

他的意思是:软件开发还停留在"手工作坊"阶段。每一个程序员都按自己的方式写代码,没有标准、没有规范、没有方法论。程序像一团乱麻,只有写它的人能看懂(而且往往三个月后连他自己也看不懂了)。修改一个bug会引入三个新bug,增加一个功能会破坏两个旧功能。整个行业就像一群没有建筑图纸的泥瓦匠,凭感觉在垒砖头——而垒出来的东西已经有几十层楼高了。

会议持续了一周。与会者们提出了各种各样的诊断和药方。有人说问题在于教育——程序员没有受过足够的训练。有人说问题在于管理——项目经理不懂技术。有人说问题在于工具——我们没有足够好的编程环境。

但有一个共识逐渐浮出水面:问题在于方法论。我们需要把"编程"从一门手艺提升为一门工程学科。

这次会议催生了一个新名词:软件工程(Software Engineering)

"工程"这个词是刻意选择的。它意味着:可预测的、可量化的、有标准的、有规范的。就像土木工程不需要依赖每一个建筑工人的个人天赋,软件工程也应该建立在一套科学原理之上,让普通的程序员也能写出可靠的代码。

这次会议没有解决软件危机——事实上,软件危机在此后几十年里以不同的形式反复出现。但它埋下了一颗种子。而这颗种子要发芽,需要一种新的编程范式。

下一章将讲述这个故事:结构化编程——一种用"顺序、选择、循环"三种基本结构来组织所有程序的方法。它的提出者,正是我们在ALGOL故事中见过的那个瑞士年轻人——Niklaus Wirth。而它的理论根基,则来自一个出乎意料的地方:数论中的一个看似无关紧要的定理。


六、两种世界观的遗产

让我们退后一步,看看ALGOL和LISP这两条路线留下的遗产。

ALGOL的遗产是秩序

它告诉我们:编程语言应该有一个精确的形式定义,每一行代码的含义都应该是明确的、无歧义的。程序应该有清晰的结构——代码块、作用域、类型——就像一座建筑应该有承重墙、横梁和地基。这种哲学直接影响了Pascal(Wirth在ALGOL基础上设计的教学语言)、C(通过BCPL和B间接继承)、Java(语法几乎是ALGOL的直系后裔)。今天世界上80%的编程语言,在语法层面都可以追溯到ALGOL 60。

ALGOL说:先定义规则,然后在规则之内创造。

LISP的遗产是自由

它告诉我们:编程语言不应该限制程序员的想象力。如果一种结构不能用语言表达,那不是语言的问题,是语言不够强大。LISP的宏系统(macro)——允许程序员在编译时扩展语言本身——是这种哲学的极致体现。你不仅仅是在用语言写程序,你还在改造语言本身来适应你的程序。

LISP说:先释放创造力,然后再谈约束。

这两种哲学之间的张力,贯穿了整个计算机科学的历史。

2002年,一个名叫保罗·格雷厄姆(Paul Graham)的程序员写了一篇著名的文章——《Beating the Averages》(战胜平均数)。格雷厄姆是LISP的狂热信徒,他在文章中说:LISP有一种"秘密武器"————是其他语言都没有的。这种能力让LISP程序员能做到其他程序员做不到的事情,就像你带着一把枪去参加一场只有冷兵器的战斗。

格雷厄姆用LISP创办了一家公司——Viaweb——后来卖给了雅虎,成为雅虎商店的前身。他用亲身经历证明:LISP不仅仅是一种学术玩具,它在商业上也能赢。

这篇文章在编程社区引发了巨大的争论。有人同意格雷厄姆,认为LISP确实被低估了。也有人嘲笑他是"LISP邪教"的传教士。但无论如何,格雷厄姆指出了一个有趣的事实:编程语言不仅仅是工具,它们塑造了你思考问题的方式。

用ALGOL系语言思考的程序员,倾向于把问题分解成结构化的模块,每个模块有明确的输入输出。用LISP思考的程序员,倾向于把问题抽象成数据和变换数据的函数,然后像搭积木一样组合它们。

一个有趣的讽刺是:ALGOL的"后代"——C、Java——统治了工业界几十年,成为企业级开发的主力。但LISP的"精神后代"——Python、JavaScript、Ruby——正在"吞噬世界"。这些语言虽然不是LISP的直接后裔,但它们继承了LISP的某些核心理念:动态类型、垃圾回收、高度的表达力、"让程序员更快乐"的设计哲学。

麦卡锡如果在天有灵,大概会微微一笑。

他在1960年代被嘲笑为"只知道喝茶的懒汉",但他坚持的那个信念——程序员的思维时间比计算机的运行时间更宝贵——最终被整个行业接受了。今天的程序员们用着带垃圾回收的语言、在Stack Overflow上讨论函数式编程、用着从LISP借鉴来的"REPL"(交互式编程环境)来调试代码——他们中的大多数人甚至不知道,这些"理所当然"的东西,都是那个画括号的男人在六十多年前发明的。


尾声

1971年,新泽西州,茉莉山市,贝尔实验室。

在一间堆满示波器和电路板的实验室角落里,两个年轻人——一个叫Ken Thompson,一个叫Dennis Ritchie——正在用一台被淘汰的PDP-7小型计算机玩一个游戏。这个游戏叫Space Travel(太空旅行),是Thompson从通用电气的大型机上移植过来的。

为了把这个游戏跑起来,他们需要一种新的编程语言——比汇编语言高级,但比PL/I简单。Thompson花了三个星期,在一张草稿纸上设计了一种新语言的雏形。Ritchie看了之后说:"我们可以做得更好。"

这种语言后来被叫做C

它融合了ALGOL的结构化思想和LISP的某些简洁性——但走了一条完全不同的路。一条极简主义的路。一条将深刻改变世界的路。

但那是另一个故事了。

第三章:屠龙者的诞生(C语言与Unix的崛起)

"C语言古怪、有缺陷,而且完全成功。"

——丹尼斯·里奇(Dennis Ritchie)


一、Multics——一座太宏伟的大教堂

1965年,新泽西州,默里山。

贝尔实验室的走廊里弥漫着咖啡和焊锡的气味。这座AT&T的研究圣殿,正沉浸在一种罕见的亢奋中——他们刚加入了一个据说将"彻底改变人与计算机关系"的项目。

它叫Multics——Multiplexed Information and Computing Service。光这名字就透着近乎宗教式的狂热。

1960年代的计算机,每台都像一座小型发电厂——占据整个房间,由穿白大褂的操作员小心伺候。你想用计算机?填申请表,排三天队,拿到几分钟上机时间。一个逗号写错?下次再来。

这就是批处理时代。计算机是神谕,程序员是祭司,普通用户连庙门都进不去。

Multics要改变这一切。

三家联合开发:MIT(理论的大脑)、通用电气GE(硬件的肌肉)、贝尔实验室(系统工程的灵魂)。目标:建造一台能同时服务几百个用户、让每个人都觉得自己独占整台机器的分时操作系统

打个比方。那时的计算机像只有一间教室的学校——每次一个班级上课,其他人在走廊排队。Multics的设计者说:我们要建一座几百间教室的超级学校,每个班级同时上课,而且每个学生都觉得整栋楼只有自己。

为此,Multics的设计极其精密,也极其复杂。它引入了层级文件系统——用目录一层层分类,像文件夹套文件夹。它支持动态链接——程序运行时才加载需要的模块。它还设计了rings安全模型——用同心圆划分权限,层层设防。

这些概念今天稀松平常——Windows、macOS、Linux、iOS、Android全在用。但在1965年,它们超前了至少十年。

问题就出在这里:太超前了。

代码量像失控的怪兽般膨胀。每增加一个新功能,就有三个旧功能崩溃。bug像地鼠——按下一个,两个从别处钻出。进度一再拖延,原本1967年交付的系统,到1968年还只是勉强能启动的骨架。MIT的教授们还在往设计文档里添加"优雅特性",贝尔实验室的工程师看着越来越厚的规格说明书,心里只有一个念头:

这玩意儿永远也完不成。

把Multics想象成一座哥特式大教堂。设计图纸美轮美奂——飞扶壁、尖拱窗、玫瑰花窗,每个细节追求极致。但施工队只有几十人,石料经常断供。建了五年,主殿穹顶还没合拢,设计者又添加了第三座钟楼。

1969年春天,贝尔实验室做出了冷酷的决定:退出Multics。

他们受够了。


二、地下室里的叛逆

肯·汤普森不是你在鸡尾酒会上会注意到的人。

个子不高,说话轻声细语,脸上总带着沉思的表情。擦肩而过你会以为他是新来的实习生。但看他写代码——完全是另一回事。

汤普森写代码像老练的木匠刨木头。没有多余线条,没有犹豫停顿,每一刀恰到好处。同事回忆说:"汤普森可以在脑子里完整运行一段程序,就像你想象走一段路一样自然。"

在Multics项目里,他负责文件系统。他比任何人都清楚系统有多臃肿。但好的想法——层级文件结构、分时处理——像种子埋在他脑子里,等待合适的土壤。

1969年夏天,土壤出现了。

贝尔实验室角落里有间不起眼的房间,堆着被淘汰的设备。在这些电子垃圾中,有一台PDP-7——DEC公司的小型机。

PDP-7今天看来原始得可笑。内存只有8K字,约34,000字节。你手机上一张自拍照约3MB——PDP-7全部内存连一张自拍都装不下。没有硬盘,用纸带阅读。连显示器都没有,接的是一台电传打字机。

但它有一个巨大优点:它是汤普森自己的。

没有项目经理,没有设计委员会,没有200页的规格说明书。只有他,和一台安静的小机器。

汤普森最初的动机说出来可能让你发笑——他想玩游戏。

《星际旅行》(Space Travel),汤普森自己在Multics上写的小程序:控制飞船在太阳系行星间飞行,计算轨道和燃料。标准的理工男白日梦。

但Multics太复杂了,跑个小游戏要启动整个分时环境,加载一堆不相关的模块,慢得抓狂。汤普森想在更简单的环境里跑它。

于是他从PDP-7开始动手。先写了图形显示驱动。然后需要文件系统存游戏数据——用周末写了个简单的层级文件系统,砍掉了Multics所有花哨功能。然后需要一个东西管理文件系统——于是写了个小小的内核(kernel),负责最基本的任务调度。

三个星期。

1969年整个夏天,汤普森利用妻子带孩子回加州探亲后的每个晚上和周末,在那台积满灰尘的PDP-7上,写出了后来被称为Unix的东西。

"Unix"这名字本身就是嘲讽——对"Multics"的戏仿:Uni(单一)对Multi(多重)。Multics是宏伟的哥特教堂,Unix是地下室里用碎石搭的小礼拜堂。简陋,但结实,而且——能用。

里奇后来回忆:"Ken花了三个星期,就做出了我们能用的系统。而我们花了三年,都没能让Multics达到同样状态。"

丹尼斯·里奇随即加入。如果汤普森是天生的工匠——沉默寡言、手到擒来——里奇就是思想家。瘦高个,戴眼镜,说话精确到近乎学术。他对编程语言有美学层面的热情,此前已参与B语言的开发。

看到汤普森搭出的简陋系统,里奇立刻意识到:这不是玩具。这是一种哲学

Multics的哲学:用复杂应对复杂。

Unix的哲学:用简单征服复杂——好的工具应该简单,简单的工具可以组合成复杂的能力。

两个性格迥异的人——沉默的工匠和优雅的思想家——开始了长达数十年的合作。他们像被大教堂拒之门外的石匠,在地下室用碎石搭了一座小教堂。他们没想到,这座小教堂最终会成为全世界最流行的建筑风格。


三、为了写Unix,顺手发明了一种语言

Unix最初用汇编语言写的。

用汇编写程序,就像用摩尔斯电码写信——每个字母都要转换成点和划。你能写,但效率极低,而且换一台机器就完全不能用了。这就是汇编的核心问题:它和特定硬件绑死在一起。 PDP-7的汇编和PDP-11完全不同,就像法语和日语。想把Unix搬到新机器上?从头来过。

他们需要一种中间语言——比汇编高级,能跨平台;又不能太高级,必须能直接操控硬件。操作系统需要管理内存、控制I/O、调度进程——这些都需要"触摸到金属"的能力。

里奇此前参与的B语言是候选者。B是BCPL的简化版,而BCPL是CPL的简化版——编程语言命名史就像一部"越来越简陋"的缩写史。

但B太简陋了。它只有一种数据类型——(word)。一切都是"字"。就像一把只有"大"这一个尺寸的扳手——能拧大多数螺丝,但遇到精密小螺丝或巨大螺母就力不从心。更致命的是,B是为解释执行设计的,速度太慢。Unix需要编译执行

里奇决定自己动手。

1972到1973年间,在那间堆满纸带和咖啡杯的办公室里,里奇在B语言基础上设计了一种全新语言。取名C——简单接在B后面,仿佛在说:"这是B的下一个版本。"

C引入了几个关键概念:

数据类型。C区分了char(字符,1字节)、int(整数,4字节)、float(浮点数)。这像给工匠配了一套不同尺寸的工具——小螺丝用小扳手,大螺母用大扳手,各司其职。

结构体(struct)。如果数据类型是单个工具,结构体就是工具箱——把不同类型的数据打包成自定义复合类型。比如定义一个"学生":姓名(字符串)、年龄(整数)、成绩(浮点数)。

函数(function)。把完成特定功能的代码打包、取名、随时调用。像菜谱里的步骤:"切丝"、"翻炒"、"加盐"——每个步骤可反复使用。

预处理器。编译前对文本进行替换。你定义一个,让简短名字代表复杂代码。像笔记本上写了缩写"老板",页边注明完整含义——每次写到"老板",读者自动脑补。

但C最独特、最强大、也最危险的特性,叫指针(pointer)。


四、C语言的美学——"信任程序员"

要理解指针,先理解内存。

想象内存是一条长街,每个门牌号里住着一个数据。创建变量 int x = 42;,编译器就在街上找个门牌号(比如1001号),把42放进去。

指针,就是那个门牌号本身。

指针变量不存数据,存的是另一个变量的地址。像一张纸条,上面只写着地址:"1001号"。拿着纸条,就能找到住户。

为什么重要?因为指针让你直接操作内存。你可以说"把1001号里的数字改成99",不需要知道住户是谁。可以说"把1001到1010号全检查一遍"——这就是数组遍历的底层原理。甚至可以说"把纸条上的地址改成1002号"——指针运算。

指针像一把万能钥匙——能打开这条街上任何一扇门,包括写着"禁止入内"的。你可以访问任何内存地址,修改任何数据,跳转到任何代码位置。这种能力在操作系统编程中不可或缺。

但这也是C最危险的地方。

C的核心哲学四个字:信任程序员

不检查数组越界。声明长度10的数组却访问第11个元素?C不报错——默默去读不属于你的内存,假装没看见。

不管内存。申请了内存,用完必须自己释放。忘了释放?内存泄漏——像租房搬走后不退房,房子空着别人也住不进。释放后又去访问?更危险——像退房后拿旧钥匙回去开门,里面住的已不是你。

不阻止你做蠢事。可以把整数强转成指针去访问随机地址。可以让两个指针指向同一块内存,通过一个修改数据,另一个完全不知情。

C像一把手术刀——锋利到极致,可完成最精密的操作。但手抖一下,就割断动脉。

里奇的设计目标不是防止程序员犯错,而是不增加不必要的限制。好的编程语言像好刀——不该有安全锁阻止你切割任何东西。锋利就是锋利,危险是锋利的另一面。

他晚年带着典型幽默说:"C语言确实给你足够的绳子来把自己吊死。但我认为,这比给你一副手铐要好。"


五、指针的陷阱

缓冲区溢出

  
char buffer[8];
  
strcpy(buffer, "这是一个非常非常长的字符串");
  

C不检查字符串是否超过8字节。多出来的部分溢出到相邻内存。如果相邻内存存着函数返回地址——程序会跳到完全错误的位置执行。

这就是缓冲区溢出(buffer overflow)——C最臭名昭著的安全漏洞。1988年,研究生罗伯特·莫里斯利用它释放了互联网史上第一个大规模蠕虫病毒,感染了当时互联网10%的计算机。

悬空指针

释放内存后,指针还保存着那个地址——但内存已不属于你。像一把失效的钥匙,房子已拆,钥匙还在手里。用它去开门,可能打开一面通往虚空的门。

段错误

Segmentation fault——每个C程序员的成人礼。当程序访问不属于自己的内存,操作系统毫不留情地把它杀掉,吐出冰冷的判词:

  
Segmentation fault (core dumped)
  

没有解释,没有道歉。法官直接敲下法槌:死刑,立即执行。

经典面试题

  
*p++;    // 什么意思?
  
(*p)++;  // 又是什么意思?
  

*p++++优先级高于*,等价于*(p++)。先取值(不用),然后把指针本身后移一位。像看了一眼1001号里的东西,然后把纸条地址改成1002号。

(*p)++:括号改变优先级。先取值,然后把值加1。指针不动。像打开1001号的门,把里面数字从42改成43,纸条上还写着1001号。

能一分钟答对?恭喜,你跨过了C的入门门槛。这就是C的魅力:用最少的符号,表达最精确的含义,代价是你必须像编译器一样思考。

那么——为什么这么危险的语言能统治世界?


六、为什么C成了霸主

1973年,里奇和汤普森做了近乎疯狂的决定:用C重写Unix内核。

此前所有操作系统都用汇编写的——操作系统需要直接操控硬件,只有汇编有这种能力。高级语言?太慢太抽象,不可能写操作系统。

他们打破了这个教条,而且成功了。C版Unix性能只比汇编版慢不到10%。更关键的是——它变得可移植了。

此前把操作系统从一种机器搬到另一种,像把房子从一块地基搬到另一块——必须拆成砖块(重写所有汇编),再重新砌起来。而C写的Unix,只需在新机器上重新编译——像把建筑图纸从中文翻译成英文,房子不用动,只换说明书。

Unix开始像野火蔓延。

转折点是贝尔实验室一个出人意料的决定。由于AT&T受1956年同意令约束,不能进入计算机市场,无法把Unix当产品卖。于是他们做了件当时看来无关紧要、事后证明改变世界的事:把Unix以极低价格甚至免费授权给大学。

这像把火种撒向干燥草原。

最先着火的是伯克利分校。1977年,研究生比尔·乔伊——后来共同创立Sun公司的年轻人——对Unix大规模改进扩展,称为BSD Unix,加入了虚拟内存、TCP/IP早期实现等。

同时,C编译器被移植到越来越多机器上。DEC的PDP系列、IBM大型机、各种新兴微处理器——只要有C编译器,Unix就能跑。

C迅速传播的三个原因:

足够简单。 C语言规范只有薄薄一本——Kernighan和里奇的经典教材仅200多页。写C编译器不太难,大学编译器课常把"写一个C编译器子集"当期末项目。

足够快。 "不需要为你不用的东西付出代价"——没有垃圾回收,没有运行时检查,每行代码直接翻译成最少的机器指令。性能接近汇编。

足够灵活。 从最高层(函数抽象、数据类型)到最底层(指针、位运算、直接内存访问)的完整工具链。写内核、写应用、写嵌入式固件、写超算程序——全能。

到1980年代,C已是全世界最流行的编程语言。

它的"后代"令人叹为观止:C++(1983,加入面向对象)、Objective-C(1984,加入消息传递)、Java(1995,语法大量借鉴C/C++)、C#(2000,微软的"改进版Java")、Go(2009,Google为云计算设计)、Rust(2010,试图在保持C性能的同时解决安全问题)——几乎所有现代语言都继承着C的语法影子。

C是程序员的第一把步枪——简单、可靠、致命。学会用它之后,其他武器只是加了瞄准镜或消音器。但扳机的手感,永远一样。


七、尾声与伏笔

镜头拉远。

今天——距汤普森在PDP-7上敲下第一行代码已过去半个多世纪——Unix的基因仍流淌在全世界每台服务器的血管里。

你每次打开网页,背后都有运行Unix(或其后代Linux)的服务器在响应。你每次刷社交媒体,数据中心里成千上万台服务器绝大多数跑着Unix家族。你每次搜索关键词,Google的服务器集群——运行定制版Linux——在几毫秒内扫描数十亿网页。

这一切的源头,是1969年夏天,一个想玩游戏的程序员,在实验室角落找到的一台废旧机器。

但故事没有结束。

1980年代,程序越来越大。Unix从几万行的小系统膨胀到百万行的庞然大物。C的过程式编程——把程序分解成函数依次调用——在小程序里如鱼得水,在大程序里开始力不从心。代码超过十万行,函数间的依赖变成纠缠的蜘蛛网,改一个函数可能导致十个看似无关的函数出问题。

程序员需要新方式——把数据和操作方法绑定在一起,像把灵魂与身体合二为一。

这种方式叫面向对象编程

它的故事,要从一个来自北欧的年轻人说起。1979年他来到贝尔实验室——没错,又是贝尔实验室——做博士后。他叫比雅尼·斯特劳斯特鲁普(Bjarne Stroustrup),来自丹麦,带着圆框眼镜和大胆想法:给C加上"类"(class),让程序员用更接近人类思维的方式组织代码。

他把新语言叫做C++——"++"是C的自增运算符,意思是"C的下一个版本"。这名字本身带着幽默——仿佛C是一杯酒,C++是往里加了一shot烈酒。

但那是下一章的故事了。


1983年,肯·汤普森和丹尼斯·里奇共同获得计算机科学最高荣誉——图灵奖。颁奖词说,他们"发展了通用操作系统理论,特别是Unix的实现"。

2011年10月12日,丹尼斯·里奇在新泽西家中去世,享年70岁。

几乎没人注意到。因为一周前——10月5日——史蒂夫·乔布斯因胰腺癌去世,全世界媒体沉浸在悼念中。

里奇和乔布斯,一个创造了Unix和C,一个创造了iPhone和iPad。前者是后者的地基——iOS和macOS底层,正是Unix和C的遗产。但在公众记忆里,地基永远不如屋顶显眼。

然而,如果你问任何一个程序员:"谁对世界影响更大?"

他们会告诉你:里奇和汤普森的遗产,比任何消费电子产品都更持久。乔布斯给了你一台设备,里奇和汤普森给了你创造设备的能力。

那台PDP-7上的小教堂,如今已是全世界的教堂。

第四章:"面向对象"的圣战(C++与Java的十字军东征)

"世界上只有两种编程语言:一种被所有人骂,另一种没人用。"

——本贾尼·斯特劳斯特鲁普(Bjarne Stroustrup)


一、当代码变成怪兽

1982年冬天,新泽西州,默里山。

贝尔实验室的暖气又坏了。几栋红砖建筑组成的研究园区里,工程师们裹着毛衣,对着终端屏幕哈气。咖啡杯堆成小山,烟灰缸满了又倒。空气里混着烟草、咖啡和电路板散发的松香味。

一间没有窗户的办公室里,一个年轻人盯着屏幕发呆。

屏幕上是一个C程序——不,准确说,是一头怪兽。代码量已膨胀到十二万行。几千个函数,几百个全局变量,数据结构和操作函数完全分离。函数在这里读那个结构体,在那里改这个数组,在另一个地方又拆那个链表。依赖关系像一团煮糊的意大利面——拉一根线头,整锅面条跟着动。

改一个bug需要三天:一天定位,一天修改,一天祈祷没有引入新bug。

想象你用Excel管理一个小卖部——几行进货、几行销售,清清楚楚。然后小卖部变超市,表格扩展到几千行。然后超市变连锁集团,几十个门店的数据全挤在一张表里。公式套公式,改一个单元格,整个表格全乱。

这就是1980年代C程序员的困境。

C的核心思想叫过程式编程——把程序拆成一个个函数,像工厂流水线:原料进来,经过一道道工序,产品出去。小项目里,这套方法优雅得像瑞士钟表。

但项目一大,齿轮太多,发条太密。函数A改了全局变量X,函数B依赖X的旧值,函数C又在另一个线程里偷偷写X。你以为是A的bug,查了三天,凶手是C。

数据和操作分离——C的设计哲学,也是它的阿喀琉斯之踵。

人类大脑的工作记忆只能同时处理七加减二个信息块。十二万行代码?几万个信息块。没有人能同时装下。

程序员需要新思维——不是把程序拆成"步骤",而是拆成"角色"。每个角色有自己的记忆和行为,角色之间通过消息交流,互不干涉。就像公司:财务部管账,技术部写代码,销售部跑客户——每个部门封装自己的事务,对外只提供服务窗口。

这种思维叫面向对象编程(Object-Oriented Programming,OOP)。

它不是1980年代的发明。1960年代挪威的Simula语言第一次引入"对象"概念,1970年代施乐帕洛阿尔托研究中心的Smalltalk把它发展成完整方法论。但它们要么太学术,要么太超前,始终没有进入主流。

直到一个丹麦人走进了贝尔实验室。


二、弑父之举——本贾尼与C++的诞生

本贾尼·斯特劳斯特卢普(Bjarne Stroustrup),1950年生于丹麦奥胡斯——安静的海港城市,以维京船博物馆和阴天的北海闻名。

他不是天才少年的叙事。更像丹麦设计本身——简洁、扎实、不张扬。奥胡斯大学读数学和计算机,剑桥读博士,论文方向是分布式系统。正是博士论文让他痛苦地意识到:现有语言不够用。他需要"类"来描述计算机行为,需要"继承"来复用逻辑,需要"异常处理"来应对网络故障。C什么都不给——只给你一把手术刀和空手术室,剩下的自己搭。

1979年,他来到贝尔实验室——那个诞生了Unix和C的圣地。他走进的,正是里奇和汤普森工作过的走廊。

他的任务:研究大型软件系统的可靠性。讽刺的是,他立刻撞上了那堵墙——C写大项目,太痛苦了。

于是他开始动手。

1979年底,在一台DEC VAX小型机上,他给C添加了一种叫**"类"(class)的机制。最初叫"C with Classes"**——名字朴实得像丹麦家具:不试图革命,只是改良。

是什么?想象设计一款游戏,需要创建一千个"士兵"。每个士兵有名字、血量、武器(数据),能移动、攻击、防御(操作)。C语言里,你用struct存数据,再写一堆函数操作数据——身体和灵魂分开管理。C++里,你定义一个Soldier,把数据和操作封装在一起——身体和灵魂重新结合。每个具体士兵是类的一个**"实例"**。类是模具,实例是铸件。

斯特劳斯特卢普给C加了三大支柱:

封装——把数据和操作打包成整体,对外只暴露必要接口。像自动售货机:投币、选商品、取货。你不需要知道里面有多少齿轮。封装隐藏复杂性,只提供服务窗口。

继承——新类继承旧类特征。写了"车辆"类,有轮子、引擎、能行驶。"汽车"继承一切,再加车门和空调。"卡车"继承一切,再加载重能力。站在父类肩膀上,不用从头写。

多态——同一指令,不同对象不同反应。对"动物"说"叫",狗汪汪,猫喵喵,鸭子嘎嘎。你不需要知道面前是什么动物——发出"叫"的指令,每个动物自己执行。

三大支柱合在一起:用模拟世界的方式来管理复杂度。 一个Customer类管客户,一个Order类处理订单,一个Payment类负责支付。每个类管好自己的事,类之间通过接口通信。

如果C是"一个人干所有活",C++就是"组建一个团队,各司其职"。

1983年,"C with Classes"改名C++++是C的自增运算符——C++意味着"C的下一个版本"。一个只有程序员才懂的冷幽默。

但老一辈C程序员不这么看。

"这是对C的亵渎!""C++太复杂了!""它违背了C的简洁哲学!"

争吵从技术论坛烧到学术会议,从实验室走廊烧到酒吧。C阵营的攻击点明确:复杂度。C整个规范薄薄一本,K&R经典教材仅200多页,关键字不到四十个。C++呢?类、继承、多态、运算符重载、引用、虚函数、模板……每加一个特性,编译器复杂度翻一番。

更让C纯粹主义者愤怒的是——C++在"弑父"。里奇创造了C,信奉"信任程序员"、"不增加不必要的限制"。斯特劳斯特卢普在C的躯体上嫁接新器官,像给一匹纯种马装上喷气发动机——马还是那匹马吗?

但C++赢了——因为它解决了真实世界的问题。当你的代码超过十万行,"简洁"不再是最高美德——"可管理性"才是。C++的抽象工具让你把百万行代码组织成几百个类,每个类职责清晰,接口明确。这不是理论上的优雅,而是工程上的救命稻草。


三、C++的"原罪"——复杂度之塔

C++像一座不断扩建的城堡。每加一个房间,就多一个可能坍塌的角落。

运算符重载——让你重新定义+-*对自定义类型的行为。可以让两个"复数"直接相加,很优雅。但也可以让+做减法——权力越大,混乱越大。

多重继承——一个类同时继承多个父类。理论上强大,实践中是噩梦——两个父类有同名方法时,编译器该听谁的?这就是臭名昭著的**"菱形继承问题"**。

模板——让函数和类操作任意类型。强大到图灵完备——你可以在编译期用模板写一个完整程序。但语法之诡异,让无数程序员在深夜对着编译器报错怀疑人生。

到1990年代,C++标准草案超过1000页

斯特劳斯特卢普自己承认:"C++的历史,就是一部与复杂度做斗争的历史。"

这话带着苦涩的坦诚。他最初只想给C加一个"类"——简单、聚焦、克制。但用户不断要求新特性,工业界不断提出新需求,标准委员会不断妥协。每加一个特性都有充分理由,加在一起,就成了复杂度之塔。

像《圣经》里的巴别塔——人类想建通天的塔,上帝让他们说不同语言,工程崩溃。C++没有崩溃,但变成了一种没有人能完全掌握的语言。没有哪个程序员敢说"我精通C++全部"。

但——全世界都在用它。

Windows核心组件用C++。Photoshop用C++。3A游戏引擎用C++。华尔街高频交易系统用C++。火箭飞控用C++。Chrome渲染引擎用C++。

为什么?因为C++在性能和抽象之间找到了独特平衡。它没有垃圾回收拖慢速度,编译出的机器码接近C的性能。同时提供足够强大的抽象工具管理百万行代码。

在游戏引擎里,每帧要在16毫秒内渲染几百万个多边形——没有余量浪费在垃圾回收上。在金融系统里,一笔交易延迟必须控制在微秒级——多一微秒可能损失百万美元。这些场景里,C++是唯一选择——不是因为优雅,而是因为没有更好的替代品。

斯特劳斯特卢普的核心原则:"零成本抽象"——你使用的抽象不应带来运行时开销。类、继承、多态,编译后应被"压平"成和手写C一样高效的机器码。

C++像一辆重型卡车。不如跑车快,不如轿车舒适——但你要运十吨钢材从上海到北京,它是唯一选择。


四、Java的"十字军东征"

1991年,加州门洛帕克。

Sun Microsystems办公楼里,一个安静的加拿大人正领导一个秘密项目。

詹姆斯·高斯林(James Gosling),1955年生于卡尔加里。12岁用汇编给医院写数据分析程序。大学时组装了自己的计算机。读博士读到一半辍学——"学术界太慢了"。同事回忆:"他写代码的速度像别人打字——不是因为他快,而是想清楚了才动手。"

1990年,Sun内部成立"Green项目"小组。目标:为下一代消费电子设备开发嵌入式语言。高斯林看到问题:C++太复杂,嵌入式设备装不下庞大的运行时。而且手动内存管理在嵌入式环境太容易出错——你不想冰箱的控制程序因为内存泄漏而死机。

他决定从头设计。最初叫**"Oak"**——因为办公室窗外有棵橡树。

设计目标明确:

简单——砍掉C++最复杂的部分。没有运算符重载,没有多重继承,没有指针。

安全——强类型检查,数组边界检查。C++里越界访问可能悄无声息毁掉内存;Java里会立刻报错——像安全气囊,撞了才弹,但至少弹了。

跨平台——最关键的创新。

传统编译:C++代码在Windows编译出.exe,在Mac编译出Mac可执行文件。同一个程序,三种机器三种版本。像把菜谱翻译成法语给法国厨师、日语给日本厨师——三种翻译,每次修改改三遍。

高斯林的做法:发明一种通用烹饪语言——不是任何国家自然语言,而是标准化"烹饪世界语"。所有厨师都学这种语言,菜谱只翻译一次。

这层"世界语"就是字节码(bytecode)。程序先编译成字节码——不针对任何特定机器的中间表示。然后每台机器安装虚拟机JVM),负责把字节码翻译成本地指令。

这就是那句口号:"Write Once, Run Anywhere"(一次编写,到处运行)。

1994年,Oak改名Java——团队成员爱喝爪哇咖啡。

1995年5月23日,Java正式发布。时机恰到好处——互联网爆发前夜。万维网刚满四岁,网景浏览器正以病毒般的速度传播。全世界意识到:计算机不再是孤岛,正连成一片大陆。而大陆上最大的问题是碎片化——用户的计算机千差万别,网站开发者想写一个跨平台应用几乎不可能。

Java像从天而降的救星。Sun做了一个关键决定:JVM免费发放。 像把教堂大门彻底敞开——不收门票,不设门槛。

Java还做了一件事让它一夜成名——Applet。嵌入网页的小程序,可以做动画、处理数据。1995年,大多数网页还是纯文本加几张图片——突然出现交互式3D图形,像在黑白电视里看到彩色画面。

媒体疯了。《连线》把Java放上封面。Sun股价像坐了火箭。

但魔法有代价。Applet太慢——每次要下载JVM、下载字节码、启动虚拟机,在56K拨号年代要等十几秒。加上严格安全沙箱让它做不了太多有用的事。

Applet最终没有成为主流。但它完成了更重要的使命——把Java推上了神坛。


五、浏览器大战——网景与微软的圣战

1995到2001年,互联网第一场真正的商业战争。

网景的Navigator浏览器占据市场80%以上。微软慌了——Windows统治PC,Office统治桌面,但在互联网上迟到了。比尔·盖茨发出著名内部备忘录**"互联网潮汐"**,宣布全面转向。

微软的反击简单粗暴:Internet Explorer,直接捆绑在Windows里。用户开机就有IE,不需要下载安装。网景的Navigator需要主动下载——大多数用户不会费心找另一个浏览器。

市场份额逆转:1996年Navigator还有70%,1999年IE追平,2001年IE占据超过85%。网景的衰落速度快得令人窒息——不是因为产品不好,而是因为对手把浏览器变成了操作系统的一部分。你不可能和一个免费且预装的产品竞争。这像开了一家餐厅,隔壁新开了一家——不仅免费,还直接建在你所有客户的厨房里。

在这场战争中,Java扮演了微妙角色。

网景需要对抗微软的"Windows垄断"。如果有一种技术无视操作系统——在Windows、Mac、Unix上都能跑——Windows的垄断就不再是优势。Java就是这种技术。

网景在Navigator里内置JVM支持。潜台词是:如果所有应用通过浏览器运行,而浏览器跨平台,操作系统就变成无关紧要的底层。

这对微软是存亡威胁。

微软反击分两步。第一步:在IE里故意把Java支持做得很差,弹出"不安全内容"警告吓退用户,同时推出自己的ActiveX——功能更强但只能在Windows上运行。

第二步:自己搞一个Java。

2000年,微软发布C#。任何见过C#代码的人都会强烈似曾相识——语法、类库、面向对象模型几乎和Java一模一样。把.cs后缀改.java,大部分代码能直接编译。

但关键区别:C#只在微软的.NET平台上运行。

"一次编写,到处运行"变成了"一次编写,只在Windows上运行"。

这是一场赤裸裸的商业截胡。Sun后来起诉微软,2001年微软赔偿2000万美元。但钱不重要——格局已定。网景2003年解散,Java Applet退出历史舞台,IE统治浏览器市场十年。

而Java,在浏览器战场败退后,在另一个战场找到了真正归宿。


六、认知崩塌——语言之争的本质

2000年代初,互联网泡沫废墟上,一个事实逐渐清晰:

Java最大的成功不在浏览器里,而在服务器端。

银行核心系统用Java重写,电信计费平台用Java搭建,电商后台用Java构建。大学课程纷纷用Java替代C++作入门语言。

为什么?三个原因:

开发效率——"一次编写到处运行"在企业环境真正发挥价值。服务器混用Linux、Solaris、AIX,Java不用改一行代码全平台跑。

人才供给——语法规范、类型安全、工具完善(Eclipse、IntelliJ IDEA),新手上手快,培训成本低。

生态繁荣——数据库连接(JDBC)、Web框架(Servlet、Spring)、消息队列(JMS)——企业需要的一切,Java生态都有现成方案。

C++则退守到最擅长的阵地:性能极致的领域——游戏引擎、操作系统内核、嵌入式系统、高频交易;存量代码的惯性——全球数十亿行C++代码在运行,迁移成本太高;硬件控制的需要——操作系统和驱动需要直接操作硬件,JVM的抽象层是障碍不是优势。

程序员终于意识到:

编程语言没有"最好",只有"最适合"。

像问"什么交通工具最好"——从北京到上海坐高铁,穿越撒哈拉骑骆驼,F1赛道开赛车,搬家运家具租卡车。

C++是赛车——速度极致,操作复杂,容错率低。

Java是高铁——稳定安全载客量大,但不够灵活。

C是摩托车——轻便快速直接,摔一跤就可能骨折。

语言之争的本质不是"哪个更先进"——而是哪个更适合这个场景,哪个有足够好的生态,哪个背后有足够强大的商业力量

Java的成功,技术只占一半。另一半是Sun的推广策略、互联网爆发的时代红利、以及"一次编写到处运行"精准命中了最痛的痛点。C++的不可替代性也不是因为优雅——斯特劳斯特卢普自己都承认C++太复杂——而是因为性能极致、存量巨大、特定领域无可替代。

这场持续二十年的"面向对象圣战",结局不是谁消灭谁——而是整个编程世界被永久改变

面向对象从C++的"异端邪说"变成行业默认范式。今天几乎所有主流语言——Java、C#、Python、JavaScript、Swift——都支持面向对象。甚至C语言程序员也在用struct加函数指针模拟面向对象——就像不信教的人也去教堂参加婚礼,不是因为信仰,而是因为习惯。

"对象"成了程序员思维的基本单元。设计系统时,第一反应不再是"第一步做什么",而是"系统里有哪些角色,每个角色有什么能力"。

这种思维方式的转变,比任何一门具体语言都更持久。


七、尾声与伏笔

2003年。互联网泡沫余烬还在冒烟。

硅谷某个车库里,孟买某间拥挤公寓里,北京中关村某个烟雾缭绕的出租屋里——一群"草根"程序员正默默写着另一种完全不同的代码。

没有大括号。没有分号。没有类型声明。没有public static void main

看起来像伪代码。

  
for name in names:
  
    print "Hello, " + name
  

它们被称为**"脚本语言"**——Python、Ruby、JavaScript、Perl。

在C++和Java信徒眼里,这些是玩具。"太慢了!""不严谨!""不能写大项目!""这不是真正的编程语言!"

这些批评在2003年完全正确。

但世界正在变化。互联网从"信息展示"变成"交互应用"。每次点击背后都需要服务器快速响应。在这种场景下,开发速度运行速度更重要。用C++写Web应用要三个月,Java一个月,Python一周。

没人想到,这些"玩具"最终会改变世界。

Python会成为人工智能和数据科学的通用语言。JavaScript会成为世界上使用最广泛的语言。Ruby会催生Rails框架,直接孕育了Twitter、GitHub等巨头。

革命从来不从中心开始。它从边缘开始。


斯特劳斯特卢普至今仍在推动C++演进——每三年更新一次标准,不断加入现代特性。他试图让C++在保持向后兼容的同时变得更简单、更安全——这像给航行中的航空母舰更换引擎。

高斯林后来离开了Sun。2010年Oracle收购Sun,也收购了Java。高斯林不久后离开Oracle——"Java的成功远超我的想象,但走向偏离了我的初衷。"

至于"Write Once, Run Anywhere"——程序员私下改成了"Write Once, Debug Everywhere"(一次编写,到处调试)。因为理论上跨平台,实际上每个平台都有微妙差异。理想丰满,现实骨感。

但这就是编程语言的真相:没有完美的语言,只有不断妥协的工程。每一门语言都是一座妥协的纪念碑——在性能与抽象之间、简洁与功能之间、安全与自由之间。

而程序员的工作,就是在这座纪念碑的阴影下,写出能运行的代码。

第五章 脚本语言的"游击队"(Python、Ruby 与 JavaScript)

"简单胜于复杂。扁平胜于嵌套。"

——蒂姆·彼得斯,《Python之禅》


一、吉多与Python——圣诞节的意外

1989年12月,阿姆斯特丹。

窗外是荷兰冬天特有的灰白色天空,雨水沿着窗玻璃缓缓滑落,像一行行没有缩进的代码。哈勒姆出生的吉多·范罗苏姆(Guido van Rossum)坐在他家客厅的旧沙发上,百无聊赖地盯着电视机。

这一年,吉多33岁。他在荷兰国家数学和计算机科学研究中心(CWI)工作,参与过ABC语言的开发——那是一门为教学设计的编程语言,优雅、简洁、对初学者友好,但在商业世界里毫无存在感。ABC失败了。吉多亲眼看着自己倾注心血的发明被锁进抽屉,落满灰尘。

圣诞节假期到了。阿姆斯特丹的街头空空荡荡,大多数商店关门歇业,连咖啡馆都挂出了"节假休息"的木牌。吉多的家人去了别处度假,他一个人留在家里。没有电视可看——好吧,有,但没什么好看的。没有朋友可以聊天。没有项目需要赶工。

他感到一种奇特的空虚。

多年后,吉多在接受采访时回忆那个下午,语气平淡得像在说天气:"我当时想,我需要找一个业余项目来打发圣诞节假期的时间。"

就是这句话,轻描淡写,像往池塘里丢了一颗小石子。但这颗石子激起的涟漪,最终变成了海啸。

吉多想写一种新的脚本语言。所谓脚本语言,你可以把它想象成一把瑞士军刀——它不像C语言那样是一把精密的手术刀,需要你先磨刀、消毒、摆好手术台;脚本语言更像是随手从口袋里掏出来的工具,咔嚓一下就能解决问题。它是"胶水",把各种现成的组件粘在一起,快速搞定任务。

吉多从ABC语言中汲取了灵感——那种对简洁和可读性的执念。但他也借鉴了C语言的很多特性,因为C在程序员中太流行了,完全不认识C的人没法在现实世界生存。他还参考了Unix shell的很多习惯用法,以及Modula-3的异常处理机制。

他把这门语言叫做Python

不是蟒蛇。不是那种缠绕猎物、慢慢绞杀的冷血动物。这个名字来自吉多最喜欢的英国喜剧团——Monty Python的飞行马戏团(Monty Python's Flying Circus)。那是一个以荒诞、无厘头著称的喜剧节目,充满了英式冷幽默。吉多觉得,Python这个名字"简短、独特,而且带点神秘感"。

于是,在一整个圣诞假期的闭关之后,1990年2月,Python 0.9.0发布了。

它有两个让人过目不忘的特点。

第一个是强制缩进。在C、Java这些语言里,代码块用大括号 {} 包裹,缩进只是"好看",写不写都行。但Python说:不,缩进就是语法的一部分。你用四个空格缩进,就意味着一个代码块的开始;你取消缩进,就意味着代码块的结束。没有大括号,没有begin...end,只有空格——沉默的、不可见的空格。

第二个是极致的可读性。Python的代码看起来不像代码,更像伪代码——那种你在白板上画给同事看的、用自然语言写成的逻辑草稿。打印一句话?print("Hello")。定义一个函数?def greet(name):。循环遍历一个列表?for item in items:

没有分号。没有大括号。没有让人眼花缭乱的符号。

吉多的美学主张可以用一句话概括:"阅读即正义"

他在后来撰写的《Python之禅》(The Zen of Python)中写道:

Beautiful is better than ugly.

Explicit is better than implicit.

Simple is better than complex.

Readability counts.

翻译过来就是:优美胜于丑陋,显式胜于隐式,简洁胜于复杂,可读性很重要。

如果C++是拉丁文——严谨、古老、每个变格都要背熟,但学会之后你可以阅读两千年前的文献——那么Python就是世界语(Esperanto):它被发明出来的目的不是为了学术上的精确,而是为了让尽可能多的人能够沟通。

只不过,世界语最终没有成为世界通用语。而Python,在二十多年后,做到了。

但那是后话。1990年代初的Python,还只是一个荷兰程序员在圣诞节打发时间的副产品。它安静地躺在学术圈里,像一颗埋在土里的种子,等待着属于它的春天。


二、缩进战争——一场持续二十年的宗教战争

如果你从未经历过编程社区的"圣战",那你可能无法想象,一群以理性自居的工程师,可以为了几个空格吵到什么程度。

Python的强制缩进,是它最鲜明的个性,也是它最大的"原罪"。

在Python之前,几乎所有主流编程语言都用某种符号来标记代码块的边界。C和Java用大括号 {},Pascal用 begin...end,Ruby用 end 关键字。缩进在这些语言里只是"排版美学"——你写不写、怎么写,编译器根本不在乎。

但Python的编译器在乎。

  
if x > 0:
  
    print("正数")
  
    print("大于零")
  
print("这行总会执行")
  

上面这段代码中,前两行 print 前面有四个空格的缩进,所以它们属于 if 的代码块——只有当 x > 0 时才会执行。第三行没有缩进,所以它在 if 外面,无论如何都会执行。

缩进就是逻辑。空格就是语法。

这在1990年代引发了轩然大波。

**"大括号派"**认为这是暴政。他们说:代码的格式应该由程序员自己决定,而不是被编译器强迫。有人喜欢缩进两个空格,有人喜欢四个,有人喜欢Tab——凭什么Python要替我做这个决定?更可怕的是,如果你不小心混用了Tab和空格,Python不会报错——它只是默默地按照不同的宽度来解释你的缩进,然后给你一个匪夷所思的bug。

**"缩进派"**反驳:你们C程序员的代码难道不缩进吗?你们的缩进难道不是和大括号保持一致的吗?既然如此,大括号就是多余的——它让你写了两遍同样的信息。Python只是把"视觉上的结构"变成了"语法上的结构",消除了歧义。你打开任何一份Python代码,它的视觉结构就是它的逻辑结构,不需要去数大括号的配对。

这场战争打了二十年,至今没有停歇。

2017年,Stack Overflow——全球最大的程序员问答社区——做了一次调查,专门问程序员:你用Tab还是Space?结果出来,Space以微弱优势胜出(大约40%对40%,其余人用混合方式或拒绝回答)。这个结果被缩进派视为"历史性胜利",被Tab派视为"统计学的耻辱"。

吉多本人的态度?他在一次采访中说了一句意味深长的话:"人们不支持用Tab,因为它们在屏幕上看起来不一样。"这句话可以有两种理解:一是Tab在不同编辑器里显示的宽度不同(有的显示4个空格,有的显示8个),导致代码看起来乱七八糟;二是吉多在暗示,人们其实并不真正理解Tab和Space的区别,只是凭感觉选边站。

2018年,吉多从Python项目"终身仁慈独裁者"(BDFL)的位置上退休。有人说,压垮他的最后一根稻草,就是关于缩进规则的无休止争论。

这场战争消耗了程序员无数的口水、博客文章和深夜论坛帖子。但没有人真正"赢"。就像所有的宗教战争一样——重要的不是谁赢了,而是你在开战之前就选好了阵营。

不过,如果你今天问一个Python程序员:"你后悔用空格吗?"他大概率会翻个白眼,然后说:"四个空格,天经地义。"


三、Ruby——松本行弘的"快乐编程"

1993年2月24日,日本大阪。

一个年轻的程序员坐在电脑前,对着屏幕叹了口气。他叫松本行弘(Yukihiro Matsumoto),朋友们叫他Matz。这一年他28岁,在一家叫"开放系统实验室"的小公司工作。

Matz和吉多有一个共同点:他也是一个对现有编程语言不满的人。但和吉多不同的是,吉多的不满是"这些语言不够简洁、不够易读",而Matz的不满更深层——他说,现有的语言不够"快乐"。

这话听起来有点玄。什么叫"快乐的编程语言"?

Matz后来解释说,他想要一种语言,让写代码的过程本身就是一种享受。不是那种苦行僧式的享受——"我克服了编译器的刁难,所以我快乐"——而是真正的、发自内心的快乐。就像弹钢琴一样,你的手指在键盘上飞舞,音乐自然流淌,你不需要思考"这个键在哪里",因为一切都在它该在的地方。

他把这个理念叫做**"最小惊讶原则"**(Principle of Least Surprise):语言的行为应该符合程序员的直觉。如果一个语法让你"惊讶"——也就是说,它的行为和你预期的不一样——那就是语言的设计有问题,不是你的问题。

Ruby就这样诞生了。它融合了Perl的灵活("想怎么写就怎么写")、Smalltalk的纯粹面向对象("一切皆对象")和Python的简洁("代码应该像诗一样")。Matz花了大约三年时间独自开发Ruby,1995年发布了第一个公开版本。

Ruby的语法有一种独特的"人情味"。比如,你想对一个数组里的每个元素做某件事,你可以这样写:

  
[1, 2, 3].each do |n|
  
  puts n * 2
  
end
  

读起来几乎像英语:"对1、2、3这个数组,对每个元素,打印它的两倍。"

再比如,Ruby允许你省略括号:

  
puts "Hello, World!"
  

而不是 puts("Hello, World!")。这些小细节让代码看起来更干净,更像自然语言。

但Ruby在很长一段时间里,只是日本程序员圈子里的"秘密武器"。它在西方世界默默无闻,直到2004年,一个叫DHH(David Heinemeier Hansson)的丹麦程序员做了一件事。

DHH当时在开发一个项目管理工具,他用Ruby写了一个Web框架,叫做Ruby on Rails(简称Rails)。Rails的核心理念是**"约定优于配置"**——你不需要写一堆配置文件告诉框架"数据库在哪里""URL怎么映射""文件放在哪个目录",因为Rails已经替你想好了。你只要按照它的"约定"来,一切就自动运转。

这就像住酒店:你不需要自己铺床、烧水、调空调温度——酒店已经帮你做好了。你只需要拎包入住。

Rails让一个人就能搭建一个完整的网站。不是那种简陋的个人主页,而是功能齐全、数据库驱动、用户系统完善的Web应用。它把原本需要一个团队干几个月的活,压缩到了一个人在一个周末就能完成。

效果是爆炸性的。

2006年,一个叫杰克·多西(Jack Dorsey)的程序员用Rails搭了一个微博客网站,叫做Twitter。同年,克里斯·万纳思(Chris Wanstrath)用Rails搭了一个代码托管平台,叫做GitHub。2006年,托比亚斯·吕特克(Tobias Lütke)用Rails搭了一个在线商店工具,叫做Shopify

Ruby on Rails成了硅谷创业者的"标配"。如果你有一个创业点子,你用Rails,三天就能上线一个MVP(最小可行产品),然后去找投资人。

但Ruby始终没有像Python那样"出圈"。它一直是程序员的语言——或者说,Web程序员的语言。生物学家不会用Ruby来分析基因序列,金融分析师不会用Ruby来做数据建模,中学老师不会用Ruby来教编程入门课。Ruby是"快乐"的,但这种快乐有一个门槛:你得先是一个对编程有热情的人。

Matz对此似乎并不介意。他在一次采访中说:"Ruby的目的不是让所有人都编程,而是让编程的人快乐。"

这句话很"日本"——谦逊、内敛、带着一点禅意。但也许正是这种"不争"的态度,让Ruby错过了成为"时代之王"的机会。


四、JavaScript——10天造出的"半成品暴君"

1995年,加利福尼亚,山景城。

网景公司(Netscape)的总部里弥漫着一种焦躁的气氛。这家公司的浏览器——Netscape Navigator——正在统治互联网,但它的创始人马克·安德森(Marc Andreessen)有一个担忧:网页太"静"了。

1995年的网页是什么样的?静态的HTML文档。你点击一个链接,页面刷新,加载新内容。你填写一个表单,点"提交",页面刷新,等待服务器的回复。一切都是"请求-刷新-等待"的循环。网页就像一本电子书——你可以看,可以翻页,但你不能和它"互动"。

安德森想让网页"活"起来。他想在浏览器里嵌入一门脚本语言,让网页可以响应用户的操作——验证表单输入、动态改变页面内容、播放简单的动画——而不需要每次都向服务器发请求。

这个任务落在了一个叫布兰登·艾克(Brendan Eich)的程序员肩上。

艾克是一个沉默寡言的技术极客,对编程语言有近乎洁癖的品味。他精通Scheme(一种函数式编程语言,以括号嵌套著称)和Self(一种基于"原型"的语言,没有类,对象直接从其他对象"克隆")。他对Java的"企业味"不太感冒,但网景的高层告诉他:这门新语言的语法必须"看起来像Java",因为Java当时是市场上最热的编程语言,网景刚和Sun公司达成了合作。

艾克被给了一个deadline:十天

十天。

十天造一门语言。

这不是工程,这是拆弹。

艾克后来回忆那段日子,语气里带着一种劫后余生的苦涩:"那十天里,我每天都在改设计,因为每天都有新的需求塞进来。语法要像Java——好。但要支持函数式编程——好。还要有对象——好。还要简单,让非程序员也能用——好。"

他把Scheme的函数式思想塞了进去(闭包、高阶函数)。他把Self的原型继承塞了进去(没有类,对象直接继承自其他对象)。他给语法披上了Java的外衣(varfunction、花括号、分号)。他加了一些"方便"的特性——比如自动类型转换,让字符串和数字可以直接相加。

十天之后,JavaScript诞生了。

它的名字里有"Java",但它和Java的关系,就像"老婆"和"老婆饼"的关系——除了名字,几乎没有共同点。这个名字纯粹是市场部门的决定,因为Java当时太火了,带上"Java"两个字好卖。

而JavaScript的"十天速成"后遗症,在此后三十年里折磨着全世界的程序员。

举几个例子。

typeof null === "object"。你问JavaScript:"null是什么类型?"它回答:"对象(object)。"但null明明表示"没有值"啊!它怎么是对象?这是因为在JavaScript最初的实现中,值在底层用一种标签(tag)来区分类型,而"对象"的标签恰好是0,null被实现为全零(空指针),所以 typeof 看到标签0,就以为它是对象。这是一个bug——一个在1995年就存在的bug。但三十年过去了,互联网上已经有数十亿行JavaScript代码依赖了这个行为,改不了了。这个bug将永远存在,直到时间的尽头。

[] + [] 等于空字符串 ""。空数组加空数组,你期望得到什么?空数组?零?都不是。JavaScript说:数组?那我先把它转成字符串。空数组转成字符串是什么?空字符串。空字符串加空字符串?还是空字符串。所以 [] + [] === ""

但更诡异的是:[] + {} 等于 "[object Object]"。空数组加空对象,JavaScript把数组转成空字符串,把对象转成 "[object Object]"(这是对象的默认字符串表示),然后拼接起来。

还有变量提升(hoisting)。你可以在声明变量之前就使用它,JavaScript不会报错——它会默默地把你的声明"提升"到函数顶部。就像你写了一封信,署名写在开头而不是结尾,但邮递员会自动帮你把署名挪到最下面。很贴心?也许。但如果你不知道这个规则,你就会对着一个 undefined 发呆半小时。

这些"怪癖"在程序员圈子里成了段子和笑料。有人专门建了网站 wtfjs.com 来收集JavaScript的" WTF 时刻"。在2000年代的前十年,"JavaScript是玩具"是编程社区的共识。

正经程序员用什么?C、C++、Java、Python。

JavaScript?那是给网页设计师用的。那些不懂"真正编程"的人,在HTML里塞几行JavaScript,做个下拉菜单、弹个警告框——仅此而已。

所有人都觉得它是个半成品。一个十天造出来的东西,能好到哪里去?

但——


五、JavaScript如何征服世界

历史最喜欢开的玩笑,就是让那些被嘲笑的人笑到最后。

2005年,两件小事改变了JavaScript的命运。

第一件:Gmail上线了。你在Gmail里点击邮件,页面不刷新,内容就加载出来了。你拖动标签,邮件在文件夹之间"飞"过去。整个体验流畅得像桌面软件——但它运行在浏览器里,用的是JavaScript。

第二件:Google Maps上线了。你用鼠标拖动地图,地图平滑地滑动,不需要一页一页地翻。你滚动鼠标滚轮放大缩小,地图像丝绸一样流畅。这背后也是JavaScript。

Gmail和Google Maps使用了一种叫做AJAX(Asynchronous JavaScript and XML,异步JavaScript与XML)的技术。简单来说,AJAX让JavaScript可以在"后台"悄悄和服务器通信——不需要刷新整个页面,只需要更新需要变化的那一小部分。

这就像你原来去餐厅吃饭,每次想加一杯水,都得站起来走到前台跟服务员说,然后等服务员把水端来,这期间你什么都不能做。但AJAX相当于给你配了一个隐形服务员——你只需要轻轻举手,水就出现在你手边,你甚至不需要中断和朋友的对话。

突然之间,网页不再是"静态文档",而是**"应用程序"**。

JavaScript从一个"让网页好看一点"的小工具,变成了"让网页变成软件"的核心技术。所有的浏览器厂商都开始认真对待JavaScript——微软优化了IE的JavaScript引擎,苹果为Safari开发了JavaScriptCore,Mozilla为Firefox打造了SpiderMonkey。

但JavaScript仍然只能在浏览器里运行。你想用JavaScript写一个服务器?不行。想用它操作文件?不行。想用它做科学计算?想都别想。它是浏览器的"囚徒"。

2009年,一个程序员瑞恩·达尔(Ryan Dahl)打破了这个牢笼。

达尔当时在思考一个问题:为什么JavaScript只能在浏览器里跑?JavaScript的引擎(V8,由Google开发)本身是一个独立的程序,它可以被嵌入到任何环境中——为什么一定要嵌入浏览器?为什么不能嵌入一个独立的运行时?

于是他写了Node.js——一个让JavaScript可以运行在服务器端的平台。它把Chrome的V8引擎从浏览器里"掏"出来,包了一层外壳,让JavaScript可以直接访问文件系统、网络、数据库——所有服务器能做的事。

Node.js的发布像一颗炸弹。

程序员们突然意识到:他们可以用JavaScript写整个应用——前端用JavaScript,后端也用JavaScript。一门语言,从浏览器到服务器,从用户界面到数据库查询,全打通。这就是所谓的**"全栈"**——一个人,一门语言,搞定一切。

2010年代,前端框架大爆发。React(Facebook,2013年)、Vue(尤雨溪,2014年)、Angular(Google)——它们让构建复杂的Web应用变得像搭积木一样。每一个框架都催生了一个庞大的生态系统,数以万计的开发者围绕它建立社区、写教程、造轮子。

NPM(Node Package Manager)——JavaScript的包管理器——变成了世界上最大的软件注册表。到2020年代,NPM上注册了超过200万个包。从解析日期的工具库到渲染3D图形的引擎,从加密算法到机器学习模型——你能想到的功能,NPM上都有一个包帮你搞定。

200万个包。这个数字意味着什么?意味着JavaScript的生态系统比任何一门语言的生态系统都庞大。它是一个帝国——一个由无数"小工具"拼凑起来的、混乱但繁荣的帝国。

JavaScript从"浏览器的玩具"变成了"全栈的暴君"。

它无处不在。网站用它,手机App用它(React Native),桌面应用用它(Electron——VS Code就是用Electron写的),物联网设备用它,甚至太空——国际空间站上的笔记本电脑运行着基于JavaScript的应用。

道格拉斯·克罗克福特(Douglas Crockford),JavaScript的"布道者",曾说过一句名言:"世界上有两种语言,一种是天天被人骂的,一种是没人用的。"

JavaScript和Python都属于前者。它们被骂得最多——被骂语法奇怪、被骂性能差、被骂设计有缺陷。但它们的用户也最多。因为"被人骂"本身就意味着"被人用"。没人骂COBOL——因为没人在用了。

JavaScript赢了。用一种最JavaScript的方式——混乱、意外、充满矛盾,但不可阻挡。

布兰登·艾克本人后来离开了Mozilla(Firefox的开发商),创办了一家加密货币公司。他在一次采访中被问到对JavaScript今天的地位有什么感想,他苦笑着说了一句:"我当年只有十天。"

十天。足够了。


六、Python的逆袭——从"胶水语言"到AI时代的王者

如果说JavaScript的逆袭是一个"灰姑娘"的故事——一个被嘲笑的丑小鸭突然变成了舞会上的女王——那Python的逆袭更像是一个"龟兔赛跑"的故事。

乌龟一直在那里,慢慢地、安静地爬着。兔子们(C++、Java)在前面跑得飞快,互相超越。没有人注意到乌龟。但乌龟从未停下。

1990年代到2000年代初,Python在编程语言的世界里是一个"温和的配角"。它不是最快的(那是C和C++),不是最流行的(那是Java),不是最酷的(那是Ruby on Rails)。但它在自己的角落里,默默地积累着用户。

它的用户是谁?不是"真正的程序员"——至少,传统意义上的"真正的程序员"不用Python。Python的用户是**"非程序员"**:生物学家用它来分析DNA序列,天文学家用它来处理望远镜数据,金融分析师用它来做量化模型,地理学家用它来处理卫星图像。

这些人不想学C++的指针和内存管理,不想和Java的类继承体系搏斗,不想配置Ruby的Gemfile。他们只想写几行代码,把数据处理好,画几张图,得出结论,然后回去写论文。

Python就是他们的"瑞士军刀"。

而Python的生态系统——那些由全球社区贡献的第三方库——是它真正的秘密武器。

想处理数据?有NumPy(2006年,Travis Oliphant发布)。NumPy让Python拥有了高效的数值计算能力——矩阵运算、傅里叶变换、随机数生成——速度接近C语言。它就像给Python装上了一个涡轮增压器。

想画图?有Matplotlib(2003年)。它可以生成各种图表——折线图、散点图、热力图、3D图——质量堪比专业绘图软件。

想分析数据?有pandas(2008年,Wes McKinney发布)。pandas提供了类似Excel电子表格的数据结构(DataFrame),但功能强大一万倍——你可以用一行代码对一百万行数据做分组、筛选、聚合。

想学机器学习?有scikit-learn(2007年)。它把几十种经典的机器学习算法封装成了简洁的API,几行代码就能训练一个分类器或回归模型。

这些库像积木一样,一块一块地叠加起来,构建起了Python在数据科学领域的"护城河"。

然后,2012年来了。

2012年,多伦多大学的杰弗里·辛顿(Geoffrey Hinton)团队用一种叫做深度神经网络的技术,在ImageNet图像识别竞赛中取得了碾压性的胜利。他们的系统叫做AlexNet——错误率比第二名低了十个百分点。

这不是渐进式的进步,这是范式革命

深度学习——一种用多层神经网络来学习数据中复杂模式的技术——突然从学术象牙塔里冲了出来,变成了全世界最热门的技术方向。而Python,因为NumPy、scikit-learn等库的存在,因为它的简洁和易用,因为它的社区生态,成了深度学习研究者和工程师的首选语言

2015年,Google发布了TensorFlow——一个开源的深度学习框架。它的前端API是Python。

2016年,Facebook发布了PyTorch——另一个深度学习框架。它的前端API也是Python。

2017年,Google DeepMind的AlphaGo击败了世界围棋冠军柯洁。AlphaGo的训练代码?Python。

一夜之间,Python成了AI时代的"官方语言"

这不是因为Python本身有什么"AI天赋"——它的底层仍然是C语言写的解释器,运行速度远不如C++。而是因为AI研究者需要快速实验、快速迭代、快速验证想法——他们不想花三天时间调试C++的内存泄漏,他们只想写十行Python代码,看看这个神经网络能不能收敛。

Python是AI研究者的"母语"。就像英语是国际学术界的通用语一样——不是因为英语是最好的语言,而是因为所有人都在用它。

2021年,Python在TIOBE编程语言指数上超越Java,成为世界第二受欢迎的编程语言(仅次于C)。2024年,它多次登顶第一名

从1989年圣诞节的业余项目,到2024年的世界第一。三十五年。

吉多·范罗苏姆对此的反应?他在一次采访中说了一句非常"吉多"的话:"这太疯狂了。我从未想到Python会成为AI的语言。我只是想写一个有趣的业余项目。"

他加入了Dropbox,后来又加入了微软。他依然是那个温和的、带着荷兰式谦逊的程序员——不像是"世界最流行语言的创造者",更像是"那个在圣诞节写了个小东西的荷兰人"。


尾声:游击队的胜利与代价

脚本语言赢了。

Python、Ruby、JavaScript——这三门语言,诞生于业余项目、十天赶工、或者一个人的执念,如今统治着互联网的大半壁江山。JavaScript运行在每一个浏览器里,Python驱动着AI革命,Ruby撑起了硅谷的创业浪潮。

它们证明了:改变世界的,不一定是那些最精密、最强大、最"正统"的工具。有时候,改变世界的,是那些最"够用"、最"顺手"、最"让人快乐"的工具。

脚本语言是编程世界的"游击队"——他们没有正规军(C/C++)的火力,没有帝国军(Java)的组织,但他们灵活、快速、无处不在。他们打的是人民战争——让每一个普通人,都能用代码表达自己的想法。

但——代价是什么?

当代码越来越多、项目越来越大、团队越来越庞大,"快速开发"的代价开始显现。

没有类型检查,一个拼写错误可以在生产环境里潜伏三个月才被发现。动态类型让重构变成噩梦——你改了函数的参数类型,整个项目里几十个调用点都可能悄悄坏掉。运行速度?Python比C慢100倍,JavaScript靠JIT编译器勉强追上,但面对大规模数值计算仍然力不从心。

NPM上200万个包,平均每个包只有几行代码——一个项目动辄依赖上千个包,一个安全漏洞就能引发连锁反应。2021年,一个叫left-pad的17行代码包被作者从NPM删除,导致成千上万个项目构建失败——整个互联网的基础设施,因为17行代码的消失而颤抖。

新一代程序员开始思考:能不能有一种语言,既有脚本语言的易用性,又有C/C++的性能和安全性?能不能有一种语言,让你写得快、跑得快、还不容易出错?

答案是肯定的。

在编程语言的下一个篇章里,一群新的"叛逆者"正在崛起。他们不满足于在"快"和"安全"之间二选一——他们要两者兼得。他们要用一种全新的方式,重新定义"系统编程"。

故事还没有结束。恰恰相反——最精彩的部分,才刚刚开始。

下一章预告:终章——Rust、Go与AI时代。当脚本语言的游击队完成了它们的使命,新的战争已经在编译器的战场上打响。内存安全、并发模型、AI辅助编程——编程语言的下一个五十年,将如何书写?

终章:新世纪的铁王座(Rust、Go 与 AI 时代的消失)

"预测未来最好的方式,就是去发明它。"

——艾伦·凯(Alan Kay)

2023年深秋,旧金山Mission District的一间咖啡馆里,一个穿连帽衫的年轻人盯着屏幕上的编译器报错发呆。终端窗口里,Rust的借用检查器又一次无情地拒绝了他的代码——红色错误信息密密麻麻铺满整个屏幕。隔壁桌,另一个程序员正在用Go写微服务,代码简洁得像一首俳句,十五分钟搞定收工,端起精酿啤酒悠然自得。

这两个人坐在同一间咖啡馆里,却活在两个完全不同的编程宇宙中。

而在咖啡馆之外,一场更大的风暴正在酝酿。2022年11月30日,ChatGPT悄然上线。它在某些时刻写出的代码,已经让一些程序员感到后背发凉。

这是编程语言故事的最后一章。也是新故事的序曲。


一、内存安全的战争

你有没有想过,你每天使用的软件——浏览器、操作系统、手机App——底层有多少是用C和C++写的?

答案是:多到令人不安。

Windows内核、Linux内核、Android底层、iOS内核、Chrome浏览器引擎、几乎所有数据库的核心……这些支撑着现代数字文明的基石,绝大部分是几十年前用C和C++搭建的。它们快如闪电,灵活如水,但也危险如裸奔在高速公路上。

为什么危险?因为C和C++给了程序员一把双刃剑——直接操作内存的权力

想象你住在一栋公寓里,每个房间代表一块内存。C语言说:"这是你的钥匙,想开哪个房间就开哪个,想拆哪面墙就拆哪面,后果自负。"大多数时候这很高效。但一旦你开错了房间——本来要进302,手一抖进了303——你就可能读到别人的隐私数据,甚至把整栋楼搞塌。

这就是缓冲区溢出(Buffer Overflow)——程序写数据超出了分配的内存边界,像往杯子里倒水,倒满了还在倒,水漫到隔壁杯子里。还有use-after-free——你归还了房间(释放了内存),但程序还在试图使用那把钥匙。房间可能已住进新客人,你的程序却在里面翻箱倒柜。

这些不是理论上的小问题。据统计,约70%的严重安全漏洞都源于内存安全问题。微软2019年的报告明确指出,过去十年修复的安全漏洞中约70%是内存安全问题。Google Chrome团队也得出类似结论。

换句话说,整个互联网的安全地基上,布满了裂缝。

于是,一场战争开始了。两个截然不同的答案浮出水面。


二、Go——Google的"简单主义"

2007年的某一天,Google总部的一间会议室里,三个人围坐在白板前。

Robert Griesemer,Google V8引擎的核心开发者。Rob Pike,贝尔实验室老兵,Plan 9操作系统的设计者。第三位——如果你了解计算机史,看到这个名字可能会倒吸一口凉气——Ken Thompson。对,就是那个Ken Thompson。Unix的联合发明人,B语言的创造者,1983年图灵奖得主。

三位加起来超过一百年编程经验,他们要解决一个很简单的问题:Google的C++代码库已经变成了一头不可驯服的怪兽。

工程师们每天要编译数小时的C++代码。模板元编程、多重继承、虚函数表……每一个特性都是一把瑞士军刀,但把二十把瑞士军刀的功能塞进一门语言,得到的不是便利,而是噩梦。Thompson曾半开玩笑地说:"我扔掉了我所有的C++代码。"

2009年11月10日,Go正式对外发布。

Go的哲学,用一句话概括:简单到令人发指。

让我给你列一份"Go没有的东西"清单:没有类继承,没有异常处理(没有try-catch,函数直接返回错误值,你自己检查,简单粗暴),没有宏,没有运算符重载,没有指针运算。2022年之前甚至没有泛型——整整十三年。

而且Go有一项"独裁"特性——gofmt。这是一个代码格式化工具,所有Go代码都必须用它来格式化。结果就是:全世界的Go代码看起来几乎一模一样。 缩进、换行、大括号的位置——全部统一。你不需要争论代码风格,因为根本没有风格可争论。

这听起来是不是有点反乌托邦?但在Google,这被视为一种解放。当所有人写的代码看起来都一样时,你读任何人的代码都不会有认知负担。

但Go真正的杀手锏,是goroutine

想象传统多线程编程:你要创建一个线程,操作系统分配几兆栈空间,上下文切换开销巨大,你还得小心翼翼地处理锁、信号量、条件变量……稍有不慎就是死锁、竞态条件——程序员的头发就是这么掉的。

goroutine说:都给我闪开。

一个goroutine只占几KB栈空间(传统线程动辄几MB)。你可以轻松启动几十万个goroutine,它们由Go的运行时调度器管理,不需要操作系统介入。两个goroutine之间通过channel通信——一条类型安全的管道,数据从一头进去,从另一头出来,优雅得像日本茶道。Tony Hoare说过:"不要通过共享内存来通信,而要通过通信来共享内存。"Go把这句话变成了现实。

如果C++是一座华丽的哥特教堂——尖拱、飞扶壁、彩色玻璃窗令人目眩——那Go就是一个标准化的集装箱。 不好看,没有装饰,甚至有点无聊。但实用、高效、可堆叠、可预测。你不需要懂建筑学就能用它盖房子。

而Google需要的正是这种"集装箱"——因为Google要盖的不是教堂,而是整个城市——数据中心的城市。

结果呢?Go成了云计算的"官方语言"。Docker——那个改变了整个软件部署方式的容器技术——用Go写的。Kubernetes——管理着全球数百万台服务器的容器编排系统——用Go写的。Terraform、Consul、Prometheus、etcd……你能叫出名字的云原生基础设施,一大半都是Go写的。

Go赢了。但它赢的方式,不是成为最耀眼的明星,而是成为空气——无处不在,但你几乎注意不到它的存在。


三、Rust——Mozilla的"极致安全"

如果说Go的故事是一首朴实的民谣,那Rust的故事就是一部重金属摇滚。

2006年,Mozilla实验室里,加拿大程序员Graydon Hoare对着一堆C++代码发愁。他在开发浏览器引擎,但内存管理问题让他抓狂——野指针、数据竞争、悬垂引用……

他想要一种语言,既像C一样快,又像Java一样安全。所有人都说这是不可能的——性能和安全,鱼与熊掌不可兼得。 Java的安全靠垃圾回收(GC),但有运行时开销和不可预测的停顿。C的快靠手动内存管理,但人类要记住每一次malloc和free,而人类——正如我们反复看到的——是非常不可靠的。

Hoare的笔记本上慢慢浮现出一个疯狂的想法:如果在编译时就能检查所有内存访问呢? 零运行时开销。

这就是Rust的核心魔法:所有权系统(Ownership System)

让我用一个比喻来解释。想象你在一个图书馆里。每本书(内存中的数据)都有一个唯一的登记人(所有者)。规则很简单:

  1. 每本书同一时间只能有一个登记人。

  2. 登记人可以把书借给别人(转移所有权)。 但一旦借出去了,登记人就不再是登记人了——书归别人管。

  3. 登记人也可以让别人"借阅"(借用),但必须在规定期限内归还。 而且,如果有人在"精读"(可变借用),其他人就不能同时借阅这本书。

当登记人离开图书馆(离开作用域),他登记的所有书都会被自动归还(释放内存)。

听起来简单对吧?但这套规则在编译器中严格执行后,产生了一个惊人的效果:几乎所有常见的内存错误——use-after-free、double-free、数据竞争——在编译阶段就被消灭了。 不需要垃圾回收,不需要运行时检查。零开销。

这就是Rust引以为傲的口号:零成本抽象(Zero-Cost Abstraction)——你用的每一个高级特性,在编译后都不会产生额外的运行时成本。你得到的安全性是"免费"的——代价是你必须在编译时通过那套严格的规则检查。

但这也意味着——Rust的编译器是你遇到过的最严厉的导师。

每一个Rust新手都会经历一个共同的仪式:与编译器搏斗(Fighting the Borrow Checker)。你写了一段代码,你觉得它是对的,逻辑上没问题,但编译器说:"不行。"你改了一处,编译器又说:"不行。"你再改,再不行。你开始怀疑人生,怀疑这门语言,怀疑自己为什么要学编程。

那种感觉,像你参加一场考试,监考老师永远不会犯错、永远不会通融。它不仅告诉你答案错了,还精确指出错在哪一行,甚至建议怎么改——但就是不会让你通过,除非你完全按它的规则来。

然而奇迹发生在某个时刻。当你终于理解了所有权、借用、生命周期的逻辑之后,你突然意识到:编译器不是在刁难你,它在教你成为一名更好的程序员。 那些它在编译时拒绝的代码,放到C++里就是运行时的定时炸弹。一旦Rust代码编译通过,它几乎不会有内存bug。这在C/C++的世界里是不可想象的。

Stack Overflow从2016年开始做年度开发者调查,Rust连续八年蝉联"最受喜爱的语言"。开发者们对它的爱,是一种"斯德哥尔摩综合征"式的爱——被编译器折磨得死去活来,但一旦驯服了它,就再也回不去了。

2024年2月,一个里程碑式的时刻:Linux内核正式接受Rust代码。 这是C语言统治内核领域三十多年来的第一次——有另一种语言被允许进入这个神圣的殿堂。Linus Torvalds,那个以暴脾气著称的芬兰人,终于松口了。不是因为Rust变得"更温柔"了,而是因为全世界都意识到:内核中的内存安全漏洞,已经不能再被忽视了。

Rust没有改变自己去迎合旧世界。是旧世界终于开始向它靠拢。


四、Go vs Rust——两种哲学的对决

现在,让我们把Go和Rust放在一起,像两把剑一样交叉比较。

它们面对同一个问题——C/C++的内存安全危机和工程复杂性——但给出了截然相反的答案。

Go说:"简单就是美。让编译器闭嘴,让程序员开心。" 它的设计哲学是做减法——砍掉泛型、异常处理、类继承,选择极致的开发效率。编译速度快得离谱,语法简单到可以速记,标准库覆盖大多数需求。代价是一些高级抽象做起来别扭,性能不是最优,泛型拖了十三年才加上。

Rust说:"安全就是美。让编译器严格,让代码无懈可击。" 它的设计哲学是做加法——加上一套前所未有的类型系统和所有权模型,然后让编译器替你把关。运行速度媲美C/C++,内存安全由编译器保证。代价是学习曲线陡峭得像一面悬崖,开发效率在初期远低于Go。

如果Go是丰田卡罗拉——可靠、经济、到处都有、谁都能开——那Rust就是特斯拉——前沿、精密、性能惊人,但你需要花时间学习它的每一个按钮,否则连车门都打不开。

但这里有一个关键事实:没有赢家。

Go统治了云基础设施——网络服务、命令行工具、微服务、API网关,Go是王者。它让你用最少的认知负担、最快的速度把东西跑起来。Rust正在攻占系统编程的最后堡垒——操作系统内核、浏览器引擎、数据库存储引擎、嵌入式系统。它让你在不牺牲性能的前提下获得前所未有的安全保障。

它们不是竞争对手,它们是互补的。就像这个世界既需要卡罗拉把人们从A送到B,也需要特斯拉去探索技术的边界。

铁王座不是一个座位。它是一群座位。而新世纪的编程语言世界,不再有一个国王。


五、AI来了——编程语言会消失吗?

2022年11月30日。

这一天,OpenAI发布了一个聊天机器人,叫ChatGPT。大多数人的反应是:"哦,又一个聊天机器人,能有多厉害?"

然后他们试了一下。

ChatGPT不仅能聊天,还能写代码。你告诉它"用Python写一个爬虫",它就真的写出来了——不是那种漏洞百出的玩具代码,而是结构清晰、注释完整、可以直接运行的生产级代码。你让它"把这段Java代码重构成更优雅的版本",它给出的方案连资深工程师都要点头。

程序员社区经历了经典的五阶段:否认——"AI写的代码肯定有bug";愤怒——"又要抢饭碗了";讨价还价——"复杂系统设计它做不了";沮丧——"等等,它怎么连LeetCode的hard题都能解";接受——"也许我需要重新思考'程序员'的含义了"。

事实上,AI辅助编程在ChatGPT之前就已经开始了。2021年,GitHub推出Copilot——一个基于OpenAI Codex模型的AI编程助手。它像一个幽灵一样坐在你的IDE里,实时预测你接下来要写什么代码,然后自动补全。第一次使用Copilot的程序员,几乎都会经历同一个瞬间:你刚写完一行函数签名,Copilot就自动补全了整个函数体——而且是对的。那种感觉,像你刚开口说上半句,搭档就接出了下半句,而且接得恰到好处。

到2024年,AI编程工具已经进化到令人瞠目的地步。它们不仅能写函数,还能理解整个项目的上下文,生成测试用例,修复bug,甚至重构架构。一些公司报告说,使用AI辅助编程后,开发效率提升了30%到50%。

于是,一个终极问题浮出水面:既然AI可以写代码,那我们为什么还要学编程语言?

让我给你一个历史视角。

编程语言的整个发展史,就是一部程序员不断远离机器、靠近人类思维的历史。

1940年代:程序员插拔电线,直接操控硬件。你写的"程序"就是物理连接。1950年代:霍珀发明编译器,人类终于可以用类似英语的指令写程序。FORTRAN诞生——第一次,数学家可以用公式而不是机器码来告诉计算机该做什么。1960年代:高级语言百花齐放。COBOL让商人用英语写程序,LISP让AI研究者用表达式思考。1970年代:C语言诞生。里奇创造了一种既接近硬件又接近人类思维的语言。1980年代:面向对象兴起。C++让程序员用"对象"来思考。1990年代:Java和Python登场,编程变得越来越平易近人。2000年代:JavaScript征服浏览器,Swift和Kotlin让手机开发变得优雅。2010年代:Go成了基础设施语言,Rust开始挑战C的统治。

看到了吗?每一次迭代,人类都在做同一件事:让自己离硬件更远一点,离自然语言更近一点。

机器码 → 汇编 → FORTRAN → C → Python → ???

下一步是什么?自然语言。

你已经在这样做了。当你告诉ChatGPT"帮我写一个Python脚本来分析CSV文件"的时候,你实际上已经在"编程"了——只不过你用的"编程语言"是英语或者中文。

编程语言并没有消失。它只是进化到了一个新的形态——提示词(Prompt)


六、从插拔电线到开口说话

让我们暂停一下,回头看看这七十年的旅程。

1946年,宾夕法尼亚大学的一间地下室里,ENIAC的程序员们——那些被历史遗忘的女性先驱们——在巨大的电子管阵列之间穿梭,用手插拔电缆来"编写"程序。一台计算机占据了整个房间,而它能做的"计算",在今天看来还不如你的智能手表。

格蕾丝·霍珀站在ENIAC前,看着那些错综复杂的电缆,心里想的是:这不对。人类不应该像电工一样工作。我们应该用语言来和机器说话。

这个想法在当时看来,和说"人类应该和鱼说话"一样荒谬。机器只懂0和1,你凭什么让它懂英语?

但霍珀没有放弃。她发明了第一个编译器,证明了人类语言可以被翻译成机器语言。从此,编程不再是电工的活,而成了作家的活。

巴科斯让科学家可以用数学公式思考。麦卡锡证明了编程可以是优雅的、递归的、接近数学之美的。汤普森和里奇奠定了整个现代计算的基石。斯特劳斯特卢普让程序员可以用对象来思考复杂系统。高斯林让程序可以在任何平台上运行。吉多让编程变得像读英语一样简单。海尔斯伯格每一次都在回答"如何让程序员更高效"这个问题。

他们不是"发明了一种工具"。他们是在回答一个哲学问题:人类应该如何与机器对话?

每个人的答案不同,但每个人都在推动人类向那个终极目标靠近一步:让机器理解人类的意图,而不是让人类去适应机器的逻辑。

七十年的旅程,我们走了多远?从插拔电线,到敲击键盘。从机器码,到自然语言。从需要四年计算机科学学位才能写程序,到对着AI说一句话就能生成代码。

编程语言的终极目标,是"让普通人不用学编程语言"。

这听起来像一个悖论——编程语言的终极目标是消灭自己。但仔细想想,这不正是每一代语言设计者一直在做的事情吗?FORTRAN消灭了机器码。C消灭了大部分汇编。Python消灭了大部分C。而AI,正在消灭Python。

不,不是消灭。是抽象。每一层新的抽象,都把底层的复杂性藏起来,让人类可以更专注于"我想做什么"而不是"我该怎么做"。

我们从插拔电线,走到敲击键盘,再到开口说话。

编程语言史,不是代码史,而是人类试图让石头(硅晶片)显灵的祷告史。

七十年来,我们一直在对着这块石头说话,从最初的比划手势(机器码),到后来的简单词汇(FORTRAN),到流利的句子(Python),再到如今的随口闲谈(Prompt)。石头终于开始"听懂"了。

这不是技术的胜利。这是人类沟通本能的胜利。


七、尾声——群星闪耀

此刻,请允许我做一个不情之请:让我们最后一次,把所有这些名字放在一起。

格蕾丝·霍珀——那个站在ENIAC前的海军女军官,她相信机器可以说人话。约翰·巴科斯——在IBM带领一群叛逆者创造FORTRAN的数学家,他让科学家从机器码的噩梦中解放出来。约翰·麦卡锡——在达特茅斯会议上提出"人工智能"概念的天才,他用LISP证明了编程可以是一种数学艺术。肯·汤普森和丹尼斯·里奇——贝尔实验室地下室里的两位隐士,他们创造了Unix和C,像两个在数字荒原上建造城市的拓荒者。比雅尼·斯特劳斯特卢普——给C穿上"面向对象"铠甲的丹麦人,他创造了C++。詹姆斯·高斯林——梦想"写一次,到处跑"的Java之父。吉多·范罗苏姆——在1989年圣诞节假期里因为"无聊"而写下Python的荷兰人,他用缩进代替大括号,无意中为AI时代铺好了路。安德斯·海尔斯伯格——从Turbo Pascal到C#到TypeScript一路创造过来的丹麦天才。Graydon Hoare——在Mozilla的角落里独自打磨Rust的加拿大人。Rob Pike和Ken Thompson——在Google用Go证明"简单"不是软弱而是一种力量的传奇。

他们不是"发明了一种工具"。

他们是七十年来,人类向机器发出的七十次对话邀请。每一次邀请,措辞不同,语气不同,但核心都是同一个问题:

"我们能不能找到一种方式,让你懂我?"

机器没有回答。但每一次尝试,都让这个问题本身变得更加清晰。而到了2024年,当AI第一次能够用自然语言理解人类的编程意图时,这个七十年的问题,终于开始有了回答的轮廓。


请允许我用一个画面来结束这本书。

想象一下,很多年以后——也许是二十年,也许是五十年。

一个大约十岁的孩子,坐在一台设备前。这台设备长什么样,我们不知道——也许是一块透明的屏幕,也许是一副眼镜,也许只是一个漂浮在空气中的全息投影。

孩子对着它说:"帮我写一个程序。"

一个声音——也许是AI的声音,也许不是——问:"什么程序?"

孩子想了想,说:"一个能让星星说话的节目。"

那个声音沉默了一秒。

在那一秒里,某个数据中心里数十亿个晶体管完成了开关,数万亿个参数被激活,承载着人类有史以来所有代码的记忆。一个程序正在从概率的海洋中浮现。

但如果你知道这七十年的故事,你会在那一秒的沉默里听到更多。

你会听到1946年,格蕾丝·霍珀在ENIAC前拔下一根电缆时的金属摩擦声。

你会听到1957年,IBM 704的打印机第一次输出FORTRAN代码时的咔嗒声。

你会听到1969年,贝尔实验室地下室里,丹尼斯·里奇敲击PDP-7键盘时的回声。

你会听到1989年的圣诞节,吉多·范罗苏姆在阿姆斯特丹的公寓里,伴着窗外的烟花,按下第一个Python缩进时的微笑。

你会听到2009年,肯·汤普森——那个已经头发花白的老人——在Google的办公室里,看着Go的第一行代码编译通过时,轻轻说了一声"好了"。

所有这些声音,所有这些身影,所有这些在深夜里与机器搏斗的灵魂——它们都在那几行代码里了。

孩子不知道这些。孩子只知道,他对着一个东西说了一句话,那个东西就开始为他创造。

而这,正是七十年来每一位编程语言先驱梦想过的未来——

一个普通人,只需要说出他想做什么,机器就能替他实现。

编程语言没有死。它只是完成了自己的使命,然后安静地退入了历史的帷幕。就像脚手架在建筑完成后被拆除,没有人会记得脚手架,但每一块砖的位置,都是它决定的。

星星开始说话了。

而我们,终于学会了如何倾听。

全书终。