[译]为什么C语言程序坚若磐石?

2008年11月11号(Single Day~~~)

C语言,在今天来说是一种特殊的编程语言。只有极少数人真的可以用C进行编程,而且我们中很大一部分人都对C有自己的看法。缓冲区溢出,栈溢出,整型数据溢出,C有很多广为人知缺陷,而这些缺陷被人们随意传播,甚至那些不熟悉C的人们。我自己已经有10年没有接触C了,由于这样或那样的原因。开始的额时候,编译器是很昂贵的(在免费的UNIX被发布之前)而且很慢,那时的环境是很糟的。而且,所有关于C的恐怖故事让我觉得我这么一个小小的普通程序员怎么可以写出可靠的C程序。

撇过一些我直接从别的地方复制粘贴过来的很多小的C模块不说,我自己写的第一个C程序是Converge VM。其中有两件事情让我惊呆了:-o 。第一,写C程序原来不是那么难。事后我才知道我年轻的时候浪费时间写汇编代码这件事在心理上给我了很大的支持,毕竟C是高级一点的汇编语言。一旦一个人理解了像指针(可以说是低级语言中最微妙的概念,因为真实世界中没有相对应的比喻)这样的概念。第二件事情是,Converge VM没有像我期待那样满是bug。

实际上,忽略可能在任何编程语言上都存在的逻辑错误,到目前为止在Converge VM中引发实际问题的只有两个只针对C才会有的错误(主意,我肯定还有很多潜伏的bug,但是我情形还没有碰上太多)。第一个错误是,一个list没有以\0(C中经典的错误),这个问题花了很长时间去调试。另一个错误则神奇的多了,花了我好几个月时间。Converge 垃圾回收器可以谨慎地根据指针回收随意分配的内存空间。在所有的现在结构中,指针都指的是字和字对齐的边界。然而,已经分配的内存块在长度上常常不是字和字对齐的。(In all modern architectures, pointers have to live on word-aligned boundaries.However, malloc'd chunks of memory are often not word-aligned in length.) 所以有时候垃圾回收器会在一个内存块位置为4的地方尝试读取4bytes,即使那个内存块是5bytes长。换句话来说,垃圾回收器尝试读入一块数据的1bytes和内存中理论上没有权限的3bytes随机数据。罕见和神奇的是,这导致的错误几乎没法解释。但是不夸张的说,在多少编程语言中一个人可以递归地加上垃圾回收器?

我和Converge VM的经历不怎么不符合我之前的偏见。我已经慢慢承认C程序会随机出现segfault,丢数据,而且常常会像Vikings(维京海盗)去Lindisfarne一样。对比来看,用高级语言编写的程序会按照正常逻辑和可以预料的模式报错。渐渐地,这些问题在我日常使用的我可以信任的这些用C写的程序中,我都碰到了。我不记得上次这些程序发生大问题的时候了。这些不会崩溃,也会优雅的处理次要的错误。就算,我对这些软件(我使用OpenBSD9年了,所以没有比这些质量更好的软件了)极度挑剔,还有一些明显的原因以至于为什么它为什么如此可靠:它被很多人使用,而这些人帮助我们找出bug。软件已经被开发出来很长时间了,所以之前的版本都存在bug。并且,坦诚一点,只有相当能干的程序员首选会倾向于C。但是,仍然存在一个根本的问题:为什么用C写的程序坚如磐石?

过了写论文这段黑暗的时期之后,我最近做了一点C编程。对于长时间没有着手写C程序的人,想妥妥地发封邮件都没谱了。这些年,我都是通过ssh在远程机器用sendmail发送邮件的。这解决了很多问题(比如黑名单),在很多网络中它也有问题(尤其是无线网络),一个过多的网络连接会被抛掉。检查邮件是否发送是很烦人的过程。所以仔细检查它的设计后,我打算写一个简单的工具集来稳妥地通过ssh发送邮件。最终的程序 - extsmail - 比我之前所期待的有更多的功能,但是最基础的思想就是通过外部的命令比如ssh简单的重试发送邮件,直到成功发送。我还想让这个工具集尽可能占用资源少还实用,还可移植。这必然决定应该用C写extsmail。然后我决定尝试尽可能地写这个程序,就当是实验吧。用传统的UNIX方式,只依赖可靠的UNIX分发版所提供的功能,而且容错能力强。在做的过程中,做了两份关于用C写程序的新手观察资料。

第一个观察不是太明显。因为用C写的程序有无数多种错误方式,我比平时更加细心。特别的,任何内存块的的操作都会引发非常危险的缓冲溢出类型错误。然而,在一个高级语言中,我可能会比较懒,心想“嗯,我索引数组的时候是不是应该给这个值减一?先跑一遍看看”。在C中,我会想“OK,坐下来想想原因”。讽刺的是,跑程序和发现问题所花的时间和坐下来思考的时间是不一样的,除了坐下来思考更加耗费脑力。

第二个观察,是我之前从没有碰到过的,在C中没有异常处理。如果,比如说extsmail,要提高容错能力,就得自己不得不处理所有可能的错误。从一方面来说,这是非常痛苦的,extsmail有很大一部分比例(大概40%)都在检查和去除错误,虽然UNIX系统方法已经很仔细的处理出错的情况了。换句话说,当在C中调用一个方法,比如stat的时候,文档会列出所有失败的情况。用户可以很容易的选择应该在程序中修复哪个情况,哪些致命错误应该进一步处理(在extsmail中,内存不足就是致命错误)。这就是在思维模式上基于语言的异常处理方式巨大的不同点,经典的哲学是正常地写代码,仅在少数情况下写try ... catch语句块来处理特定错误(很少碰到的错误)。Java,用受控制的异常,以不同的方式告诉用户“当调用这个方法的时候,你需要try catch特定的异常”。

我明白了一件事情,当希望软件足够的强壮(鲁棒性)的时候,基于异常的软件设计是不合适的。而需要明确的是知道一个方法返回的或者抛出的错误或者异常,然后根据情况去处理。在现在的IDE中,可以根据写的方法自动显示会抛出哪些异常,最多也就只能做到这一点了。理论上,面向对象中的子类和多态意味着预编译的库不能根据写的代码确定会不会抛异常(因为子类会重写方法,会抛出不同的错误)。从实用方面来说,我怀疑这么多方法都会抛出很多不同的异常,这会让用户懵了。对比UNIX中的方法,就非常注意,它们会尽量减少返回给用户的错误的数量,和一些内部错误,或者收集归类错误。进一步,我也怀疑那些依赖异常处理的很多库需要大幅度的进行重写来减少抛出的异常数量直到一个合理的数值。再进一步,方法的调用者应该决定什么错误应该次要的,可以恢复的,哪些会导致重要的问题,甚至导致程序结束;受控制的异常,被调用者强制处理的异常,就忘了这一点。

Henry Spencer 说,“那些不懂UNIX的人注定要可怜地重新发明轮子”。这就是为什么这么多用C写的程序比我们所提出的偏见更加坚固,UNIX文化,在计算机主流里,最古老和最明智的文化,已经发现很多把C的局限和缺陷变成优势的方法。就像我经历的,慢慢地我才明白了这点。综上,如果没有经过大量的思考,我不建议使用C。如果使用C,那最终的软件坚如磐石,但开发会花费大量人力。

Source:http://tratt.net/laurie/blog/entries/how_can_c_programs_be_so_reliable

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,772评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,458评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,610评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,640评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,657评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,590评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,962评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,631评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,870评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,611评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,704评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,386评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,969评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,944评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,179评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,742评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,440评论 2 342

推荐阅读更多精彩内容