你有没有想过,为什么编程语言里有那么多名字带”C”的家伙?C、C++、C#——它们看起来像是一家人,实际上也确实是一家人,只不过这个家族故事远比你想象的更精彩,也更荒诞。
一切的起点:一个想打游戏的程序员
故事要从上世纪六十年代末的贝尔实验室说起。那时候 Ken Thompson 和 Dennis Ritchie 正在一个叫 Multics 的操作系统项目上干活。这个项目野心很大,想做一个划时代的分时操作系统,但后来贝尔实验室觉得这玩意儿又贵又慢,于是撤资不干了。
然而 Thompson 同学并不想就此罢手——倒不完全是因为他有多崇高的技术理想,而是因为他在 Multics 上写了一个叫 Space Travel 的小游戏,他还想继续玩。于是他翻出实验室角落里一台吃灰的 PDP-7 小型机,把游戏移植了上去。在这个过程中,他顺手搓出了一套工具集,这些工具后来逐渐演变成了一个操作系统。1970 年,Brian Kernighan 给它取了个名字——Unix,算是对那个庞大臃肿的 Multics 开了个玩笑(”Uni”暗示”单一”,与 Multics 的”Multi”相对)。
但写操作系统光靠汇编语言太痛苦了,Thompson 于是在一种叫 BCPL 的语言基础上创造了 B 语言。可惜 B 语言功能太简陋,Dennis Ritchie 拿过来一顿改造——加了数据类型,加了指针,加了各种特性——到 1972 年左右,改得面目全非了,于是改了个名字叫 C。
这个命名其实非常朴素:BCPL → B → C,就是字母表往后走了一位。没有什么深层含义,没有递归的自指幽默,就是单纯地”接着来”。有人后来开玩笑说,按这个逻辑下一个应该叫 D——还真有人后来做了一个叫 D 的语言。
到了 1973 年,Unix 的内核被用 C 语言完全重写了。这件事的意义怎么强调都不为过:它证明了操作系统可以用高级语言来写,而不必绑死在某一种硬件的汇编语言上。从此,C 语言搭着 Unix 的东风横扫了整个计算机世界。1978 年,Kernighan 和 Ritchie 合著了那本《C 程序设计语言》,俗称”K&R”,这本薄薄的小书成了好几代程序员的启蒙圣经。
C++:当 C 遇上面向对象
时间来到 1979 年。一位叫 Bjarne Stroustrup 的丹麦小伙在贝尔实验室做博士论文相关的工作。他之前用过一种叫 Simula 的语言——这是世界上第一个支持面向对象编程的语言,写大型程序特别顺手,但是慢得令人发指。他也用过 C,速度飞快,但写复杂程序时缺少组织代码的好手段。
Stroustrup 的想法很简单也很大胆:把 Simula 的面向对象特性搬到 C 上来。他最初搞出来的东西叫”C with Classes”——直译就是”带类的 C”,名字朴实得令人感动。
他写了一个叫 Cfront 的工具,这个工具本质上是一个预处理器,把”带类的 C”代码翻译成普通的 C 代码,然后再用 C 编译器编译。有趣的是,Cfront 本身大部分就是用”带类的 C”来写的——这就像一只猫试图抓住自己的尾巴。
1983 年,这个语言需要一个正式的名字。之前它被叫过”new C”、”C84″,都不太好听。最终,Stroustrup 的同事 Rick Mascitti 提议叫它 C++。在 C 语言里,++ 是自增运算符,意思是”加一”,所以 C++ 的意思大致是”C 再进一步”。据说 Mascitti 后来坦言,这名字纯粹是开玩笑想出来的。但不管怎样,这个玩笑被全世界记住了。
顺便提一句,有些人开过另一个玩笑:如果你写 C++ 的话,按照 C 语言的后缀自增语义,它先返回 C 的旧值,再加一。所以 C++ 其实还是 C——你得写 ++C 才能真正得到”进一步的 C”。这种只有程序员才懂的冷笑话,大概就是编程圈子的浪漫吧。
Stroustrup 后来在很多场合被人问到各种匪夷所思的问题。他甚至专门在自己的 FAQ 页面上澄清:他的名字是 Bjarne,不是 Bjorn、Bjørn 或者 Barney;姓是 Stroustrup,不是 Stroustroup、Strustrup、Straustrup 或者其他什么变体——他说用 Google 搜索能找到以上每一种错误拼法的文献。
C#:微软的”不是 Java”
如果说 C++ 是 C 的亲生儿子,那 C# 的身世就要复杂得多了。它更像是 C 家族的养子——虽然继承了名字和一些家族基因,但它的诞生动机其实来自一场商业战争。
上世纪九十年代,Sun Microsystems 的 Java 语言势头正猛,”一次编写,到处运行”的理念吸引了大批开发者。微软眼红了,于是搞了一个叫 Visual J++ 的东西——本质上是微软自己的 Java 实现,但加了一堆微软独有的扩展。Sun 不干了,一纸诉状告上法庭,理由是微软的实现没通过 Java 合规测试,违反了授权协议。微软败诉,被迫停止了 J++ 的开发。
吃了这个亏之后,微软决定不再看别人脸色,自己搞一个新语言。1999 年,Anders Hejlsberg 组建了一个团队,开始开发一个全新的语言。这位 Hejlsberg 可不是一般人——他之前设计过 Turbo Pascal 和 Delphi,是编程语言界响当当的人物。
这个新语言最初的代号叫 COOL,全称是 C-like Object Oriented Language(类 C 的面向对象语言)。团队挺喜欢这个名字的,他们的源代码文件扩展名一度就是 .cool。但后来发现”Cool”这个词不好注册商标——毕竟太通用了。
于是改名大会开始了。候选名字包括:e-C、Safe C、C-square、C-cube、C-prime、C-star、Cesium……看看这些名字,不得不说最终选中的 C# 确实是矢量空间里的最优解。
C# 这个名字有几层含义。最表面的一层来自音乐:在乐理中,sharp(♯)表示升半音,意味着”比原来的音高一点”。类比到编程语言,C# 就是”比 C 高一点”的语言。
但更有趣的是第二层含义:如果你把四个加号(+)紧凑地排列成 2×2 的方阵,看起来就像一个井号(#)。所以 C# 某种意义上可以理解为 C++++,也就是”比 C++ 更进一步”。
说到那个井号,这里还有一个小尴尬。C# 里的”#”其实应该是音乐里的升号 ♯(Unicode U+266F),但由于标准键盘上没有这个符号,大多数字体也不支持,所以大家都用数字符号 #(U+0023)来代替。微软在正式的广告和包装上会尽量用真正的升号 ♯,但在代码和日常交流中,大家早就习惯了打 # 号。所以严格来说,我们每天打的都是”C 井号”而不是”C 升号”——但千万别在程序员面前把它读成”C pound”(C 英镑)或者”C hashtag”(C 话题标签),否则你会收获一堆白眼。
2000 年,C# 随着 .NET 框架在 Professional Developers Conference 上正式亮相。Java 之父 James Gosling 对此评价辛辣,说 C# 基本上就是”删掉了可靠性、生产力和安全性的 Java”。而 Hejlsberg 则反驳说 C# 不是 Java 的克隆,在设计上其实更接近 C++。二十多年过去了,C# 和 Java 确实走上了截然不同的发展道路,各自精彩。
C# 早期还有一个官方吉祥物,名叫 Andy——以 Anders Hejlsberg 命名。但这个吉祥物在 2004 年就被退役了,存在感低到几乎没人记得它长什么样。
它们到底是什么关系?
理清了历史,我们可以画一张家族谱系图:
BCPL 是祖父辈,B 是父亲,C 是长子。C++ 是 C 的直系后代——它几乎完全兼容 C,在 C 的基础上加了面向对象等特性。而 C# 则更像是一个受 C/C++ 启发、但另起炉灶的新语言,骨子里跟 Java 的渊源可能比跟 C 的更深。
从技术层面看,C 是一门极其接近硬件的语言,给你最大的自由,也给你最多的射击自己脚趾的机会。C++ 在 C 的基础上加了抽象层,但保留了底层操控能力——它是出了名的复杂,ISO 标准文档有几百页。C# 则更”高级”,有垃圾回收、没有指针(至少默认没有)、运行在虚拟机上,写起来舒服很多,代价是离硬件更远了一步。
一个形象的比喻:C 是一把瑞士军刀,锋利但容易割到自己;C++ 是一把装了安全锁的瑞士军刀,但说明书有八百页;C# 是一把电动螺丝刀,好用省力,但你别想拿它去野外求生。
一些散落的趣闻
说到 C 家族,有一些零碎的趣事值得一提。
Dennis Ritchie 在 2011 年 10 月 12 日去世,而 Steve Jobs 在同年 10 月 5 日去世——仅仅早了一周。Jobs 的离世引发了全球性的哀悼浪潮,铺天盖地的新闻报道几乎占据了所有媒体版面。相比之下,Ritchie 的去世几乎没有引起公众注意。但讽刺的是,Jobs 赖以成功的那些产品——Mac、iPhone、iPad——它们的操作系统底层都是用 C 语言写的,运行在 Unix 的后代系统上。Google 的工程师 Rob Pike 说过一段话,大意是:互联网上几乎所有东西都建立在 C 和 Unix 之上——浏览器是 C 写的,服务器内核是 C 写的,就算不是直接用 C,也是用 C 的后代语言写的。Ritchie 的名字不是家喻户晓,但如果你用显微镜去看任何一台电脑的内部,到处都能看到他的工作。
还有一件事:Ken Thompson 和 Dennis Ritchie 的合作关系好到什么程度呢?Thompson 在图灵奖演讲中回忆说,他们合作十年来只出过一次工作重叠——那次他们发现两个人不约而同地写了同一段二十行的汇编程序。
C++ 的创始人 Stroustrup 有一句被广泛引用(但据他本人说经常被断章取义)的设计哲学:让 C++ 尽可能接近 C,但不要更接近。这听起来像一句禅语,但背后的考量很务实——他希望 C 程序员能很容易地迁移到 C++,但同时也希望 C++ 能摆脱 C 的一些历史包袱。
在 .NET 的生态里,”sharp”这个后缀几乎成了一种传统。除了 C#,微软和社区还搞出过 J#(基于 Java)、A#(基于 Ada)、F#(函数式编程语言)、Eiffel#(基于 Eiffel)。连一些库也跟风叫了 Gtk#、Cocoa# 之类的名字。如果有一天出现了 Python#,我大概也不会太意外。
最后
从 1972 年的 C,到 1983 年的 C++,再到 2000 年的 C#,这个家族跨越了半个世纪的计算机史。它们之间的关系有亲缘也有竞争,有传承也有叛逆。C 是那个沉默的老父亲,你看不见他,但他的 DNA 无处不在;C++ 是继承了父业又开疆拓土的长子,能力很强但脾气也很大;C# 是在另一座城市长大的小弟,走了一条不同的路,但骨子里的某些东西还是像的。
它们共同构成了现代计算的基石。你此刻用来读这篇文章的设备,无论是手机、电脑还是平板,它的操作系统内核大概率是 C 或 C++ 写的;你访问的网站后端可能跑着 C# 的代码;你刷的短视频的编解码器几乎一定是 C/C++ 实现的。
这大概就是 C 家族最厉害的地方——你可能从来没学过 C,但你一辈子都在用它。

发表回复