译文原地址程序员
http://blog.jobbole.com/113828/编程
英文原地址小程序
http://esr.ibiblio.org/?p=7724缓存
个人上一篇博文《与 C 语言长别离》引来了个人老朋友,一位 C++ 专家的评论。在评论里,他推荐把 C++ 做为 C 的替代品。这是不可能发生的,若是 C++ 代替 C 是趋势的话,那么 Go 和 Rust 也就不会出现了。安全
可是我不能只给个人读者一个光秃秃的见解(LCTT 译注:此处是双关语)。因此,在这篇文章中,我来说述一下为何我再也不碰 C++ 的故事。这是关于计算机语言设计经济学专题文章的起始点。这篇文章会讨论为何一些真心很差的决策会被作出来,而后进入语言的基础设计之中,以及咱们该如何修正这些问题。数据结构
在这篇文章中,我会一点一点的指出人们(固然也包括我)自从 20 世纪 80 年代以来就存在的关于将来的编程语言的预见失误。直到最近,咱们才找到了证实咱们错了的证据。并发
我记得我第一次学习 C++ 是由于我须要使用 GNU eqn 输出 MathXML,而 eqn 是使用 C++ 写的。那个项目不错。在那以后,21 世纪初,我在韦诺之战Battle For Wesnoth那边当了多年的资深开发人生,而且与 C++ 相处甚欢。编程语言
在那以后啊,有一天咱们发现一个不当心被咱们授予提交权限的人已经把游戏的 AI 核心搞崩掉了。显然,在团队中只有我是不那么惧怕查看代码的。最终,我把一切都恢复正常了 —— 我折腾了整整两周。再那以后,我就发誓我不再靠近 C++ 了。工具
在那次经历事后,我发现这个语言的问题就是它在尝试使得原本就复杂的东西更加复杂,来粗陋补上由于基础概念的缺失形成的漏洞。对于裸指针这样东西,它说“别这样作”,这没有问题。对于小规模的我的项目(好比个人魔改版 eqn),遵照这些规定没有问题。性能
可是对于大型项目,或者开发者水平良莠不齐的多人项目(这是我常常要处理的状况)就不能这样。随着时间的推移以及代码行数的增长,有的人就会捅篓子。当别人指出有 BUG 时,由于诸如 STL 之类的东西给你增长了一层复杂度,你处理这种问题所须要的精力就比处理同等规模的 C 语言的问题就要难上不少。我在韦诺之战时,我就知道了,处理这种问题真的至关棘手。
我给 Stell Heller(个人老朋友,C++ 的支持者)写代码时不会发生的问题在我与非 Heller 们合做时就被放大了,我和他们合做的结局可能就是我得给他们擦屁股。因此我就不用 C++ ,我以为不值得为了其花时间。 C 是有缺陷的,可是 C 有 C++ 没有的优势 —— 若是你能在脑内模拟出硬件,那么你就能很简单的看出程序是怎么运行的。若是 C++ 真的能解决 C 的问题(也就是说,C++ 是类型安全以及内存安全的),那么失去其透明性也是值得的。可是,C++ 并无这样。
咱们判断 C++ 作的还不够的方法之一是想象一个 C++ 已经搞得不错的世界。在那个世界里,老旧的 C 语言项目会被迁移到 C++ 上来。主流的操做系统内核会是 C++ 写就,而现存的内核实现,好比 Linux 会渐渐升级成那样。在现实世界,这些都没有发生。C++ 不只没有打消语言设计者设想像 D、Go 以及 Rust 那样的新语言的想法,它甚至都没有取代它的前辈。不改变 C++ 的核心思想,它就没有将来,也所以,C++ 的抽象泄露leaky abstraction也不会消失。
既然我刚刚提到了 D 语言,那我就说说为何我不把 D 视为一个够格的 C 语言竞争者的缘由吧。尽管它比 Rust 早出现了八年(和 Rust 相比是九年)Walter Bright 早在那时就有了构建那样一个语言的想法。可是在 2001 年,以 Python 和 Perl 为首的语言的出现已经肯定了,专有语言能和开源语言抗衡的时代已通过去。官方 D 语言库/运行时和 Tangle 的无谓纷争也打击了其发展。它从未修正这些错误。
而后就是 Go 语言(我原本想说“以及 Rust”。可是如前文所述,我认为 Rust 还须要几年时间才能有竞争力)。它的确是类型安全以及内存安全的(好吧,是在大多数时候是这样,可是若是你要使用接口的话就不是如此了,可是自找麻烦可不是正常人的作法)。个人一位好友,Mark Atwood,曾指出过 Go 语言是脾气暴躁的老头子由于愤怒而创造出的语言,主要是 C 语言的做者之一(Ken Thompson) 由于 C++ 的混乱臃肿形成的愤怒,我深觉得然。
我能理解 Ken 恼火的缘由。这几十年来我就一直认为 C++ 搞错了须要解决的问题。C 语言的后继者有两条路可走。其一就是 C++ 那样,接受 C 的抽象泄漏、裸指针等等,以保证兼容性。而后以此为基础,构建一个最早进的语言。还有一条道路,就是从根源上解决问题 —— 修正 C语言的抽象泄露。这一来就会破环其兼容性,可是也会杜绝 C/C++ 现有的问题。
对于第二条道路,第一次严谨的尝试就是 1995 年出现的 Java。Java 搞得不错,可是在语言解释器上构建这门语言使其不适合系统编程。这就在系统编程那留下一个巨大的洞,在 Go 以及 Rust 出现以前的 15 年里,都没有语言来填补这个空白。这也就是个人 GPSD 和 NTPsec 等软件在 2017 年仍然主要用 C 写成的缘由,尽管 C 的问题也不少。
在许多方面这都是很糟糕的状况。尽管因为缺乏足够多样化的选择,咱们很难认识到 C/C++ 作的不够好的地方。咱们都认为在软件里面出现缺陷以及基于安全方面考虑的妥协是理所固然的,而不是想一想这其中多少是真的因为语言的设计问题致使的,就像缓存区溢出漏洞同样。
因此,为何咱们花了这么长时间才开始解决这个问题?从 C 1972 年面世到 Go 2009 年出现,这其中隔了 37 年;Rust 也是在其仅仅一年以前出现。我想根本缘由仍是经济。
从最先的计算机语言开始,人们就已经知道,每种语言的设计都体现了程序员时间与机器资源的相对价值的权衡。在机器这端,就是汇编语言,以及以后的 C 语言,这些语言以牺牲开发人员的时间为代价来提升性能。 另外一方面,像 Lisp 和(以后的)Python 这样的语言则试图自动处理尽量多的细节,但这是以牺牲机器性能为代价的。
广义地说,这两端的语言的最重要的区别就是有没有自动内存管理。这与经验一致,内存管理缺陷是以机器为中心的语言中最多见的一类缺陷,程序员须要手动管理资源。
当相对价值断言与软件开发在某个特定领域的实际成本动因相匹配时,这个语言就是在经济上可行的。语言设计者经过设计一个适合处理如今或者不远的未来出现的状况的语言,而不是使用现有的语言来解决他们遇到的问题。
随着时间的推移,时兴的编程语言已经渐渐从须要手动管理内存的语言变为带有自动内存管理以及垃圾回收(GC)机制的语言。这种变化对应了摩尔定律致使的计算机硬件成本的下降,使得程序员的时间与以前相比更加的宝贵。可是,除了程序员的时间以及机器效率的变化以外,至少还有两个维度与这种变化相关。
其一就是距离底层硬件的距离。底层软件(内核与服务代码)的低效率会被成倍地扩大。所以咱们能够发现,以机器为中心的语言向底层推动,而以程序员为中心的语言向着高级发展。由于大多数状况下面向用户的语言仅仅须要以人类的反应速度(0.1 秒)作出回应便可。
另外一个维度就是项目的规模。因为程序员抽象发生的问题的漏洞以及自身的疏忽,任何语言都会有可预期的每千行代码的出错率。这个比率在以机器为中心的语言上很高,而在程序员为中心的带有 GC 的语言里就大大下降。随着项目规模的增大,带有 GC 的语言做为一个防止出错率不堪入目的策略就显得愈发重要起来。
当咱们使用这三种维度来看当今的编程语言的形势 —— C 语言在底层,蓬勃发展的带有 GC 的语言在上层,咱们会发现这基本上很合理。可是还有一些看似不合理的是 —— C 语言的应用不合理地普遍。
我为何这么说?想一想那些经典的 Unix 命令行工具吧。那些小程序一般均可以使用带有完整的 POSIX 支持的脚本语言快速实现出来。从新编码那些程序将使得它们调试、维护和拓展起来都会更加简单。
可是为何仍是使用 C (或者某些像 eqn 的项目,使用 C++)?由于有转换成本。就算是把至关小、至关简单的程序使用新的语言重写而且确认你已经忠实地保留了全部非错误行为都是至关困难的。笼统地说,在任何一个领域的应用编程或者系统编程在一种语言的权衡过期以后,仍然坚持使用它。
这就是我和其余预测者犯的大错。 咱们认为,下降机器资源成本(增长程序员时间的相对成本)自己就足以取代 C 语言(以及没有 GC 的语言)。 在这个过程当中,咱们有一部分或者甚至一大部分都是错误的 —— 自 20 世纪 90 年代初以来,脚本语言、Java 以及像 Node.js 这样的东西的兴起显然都是这样兴起的。
可是,竞争系统编程语言的新浪潮并不是如此。 Rust 和 Go 都明确地回应了增长项目规模 这一需求。 脚本语言是先是做为编写小程序的有效途径,并逐渐扩大规模,而 Rust 和 Go 从一开始就定位为减小大型项目中的缺陷率。 好比 Google 的搜索服务和 Facebook 的实时聊天复用。
我认为这就是对 “为何再也不早点儿” 这个问题的回答。Rust 和 Go 实际上并不算晚,它们相对迅速地回应了一个直到最近才被发现低估的成本动因问题。
好,说了这么多理论上的问题。按照这些理论咱们能预言什么?它告诉咱们在 C 以后会出现什么?
推进 GC 语言发展的趋势尚未扭转,也不要期待其扭转。这是大势所趋。所以:最终咱们将拥有具备足够低延迟的 GC 技术,可用于内核和底层固件,这些技术将以语言实现方式被提供。 这些才是真正结束 C 长期统治的语言应有的特性。
咱们能从 Go 语言开发团队的工做文件中发现端倪,他们正朝着这个方向前进 —— 可参见关于并发 GC 的学术研究 —— 从未中止研究。 若是 Go 语言本身没有选择这么作,其余的语言设计师也会这样。 但我认为他们会这么作 —— 谷歌推进他们的项目的能力是显而易见的(咱们从 “Android 的发展”就能看出来)。
在咱们拥有那么理想的 GC 以前,我把能替换 C 语言的赌注押在 Go 语言上。由于其 GC 的开销是能够接受的 —— 也就是说不仅是应用,甚至是大部份内核外的服务均可以使用。缘由很简单: C 的出错率无药可医,转化成本还很高。
上周我尝试将 C 语言项目转化到 Go 语言上,我发现了两件事。其一就是这活很简单, C 的语言和 Go 对应的很好。还有就是写出的代码至关简单。因为 GC 的存在以及把集合视为首要的数据结构,人们会预期代码减小,可是我意识到我写的代码比我最初指望的减小的更多,比例约为 2:1 —— 和 C 转 Python 相似。
抱歉呐,Rust 粉们。大家在内核以及底层固件上有着美好的将来,可是大家在别的 C 领域被 Go 压的很惨。没有 GC ,再加上难以从 C 语言转化过来,还有就是 API 的标准部分仍是不够完善。(个人 select(2)
又哪去了啊?)。
对大家来讲,惟一的安慰就是,C++ 粉比大家更糟糕 —— 若是这算是安慰的话。至少 Rust 还能够在 Go 顾及不到的 C 领域内大展宏图。C++ 可不能。