荔园在线

荔园之美,在春之萌芽,在夏之绽放,在秋之收获,在冬之沉淀

[回到开始] [上一篇][下一篇]


发信人: EDE (Thinker醒客), 信区: SoftDev
标  题: [转] 编写优质无错C程序秘诀(序)
发信站: 荔园晨风BBS站 (Thu Mar  9 21:38:25 2006), 站内

原文来自: http://blog.csdn.net/lovetangtang/archive/2006/03/09/619841.aspx

1986年,在为几家小公司咨询和工作了10年之后为了获得编写Macintosh应用程序的经验,
我特意到Microsoft公司工作,参加了Macintosh开发小组。这个小组负责Microsoft的图形
电子表格应用程序的开发。
当时,我还不能肯定想象的代码是什么样子的,我想也许应该既引入入胜又雅致吧!但我看
到的代码却很平常,与我以往见到的其它代码没有什么不同。要知道,Excel有一个相当漂
亮的用户界面 ─── 它比当时其它基于字符的电子表格软件更容易使用,更加直观。但使
我感受更深的是产品中包含的一个多功能调试系统。
该系统旨在自动地问程序员和测试者进行错误报警。其工作方式非常象波音747驾驶仓内向
驾驶员报告故障的报警灯。该调试系统主要用来对代码进行监视,它并不过多地对代码进行
测试。虽然现在该调试系统采用的概念已不再新鲜了,但当时它们的广泛使用程度以及该系
统有效的查错能力还是吸引了我,使我深受启发。没过多久,我就发现Microsoft的大部分
项目都有多功能的内部调试系统,而Microsoft的程序员都高度重视代码中的错误及其产生
原因。
在做了两年Macintosh Excel之后,我离开了该开发小组,去帮助另一个代码错误数目超常
的小组。在开发Excel的两年中,我发现Microsoft虽然壮大了两倍,但许多老项目组熟知的
概念并没有随着公司的壮大而传到新项目组。新程序员不象我加入Microsoft之前时的老程
序员一样对容易引起错误的编码习惯特别重视,而只有一般的注意。
在我转到新项目组六个月之后,有一次我对一个程序员伙伴提到:“应该把编写无错代码的
某些概念写成文字,使那些原理能在新项目组传开”。这时另一位程序员对我说:“你不要
总是想着写文档,为什么你不把这一切都写下来?为什么你不写本书,问问Microsoft出版
社是否愿意出版呢?毕竟这些信息不是谁的专利,其作用不过是为了使程序员更加重视错误
。”
当时我对这个建议并没有多想,主要原因是没有时间,而且以前我也没有写过书。以前我所
做过与写书最有关系的事情,不过是在80年代初协助别人主办Hi-Res杂志的程序设计专栏,
这与写书毕竟不是一回事。
正如您所见到的,这本书还是写出来了。理由很简单:1990年,由于错误越来越多,
Microsoft取消了一个尚未公布的产品。现在,错误越来越多已经不是什么新鲜事情,
Microsoft的几个竞争者都因此取消过一些项目。但Microsoft因为这种原因取消项目,还是
头一次。最近,随着接连不断地出现产品错误。管理人员终于开始叫嚷“受不了啦”,并采
取了一系列的措施企图将错误率下降到原先的水平。尽管如此,仍然没有人将这些错误因由
记录下来。
现在,Microsoft已经比我刚进公司时大了九倍。很难设想,倘若没有准确的指南,公司怎
样才能将出错率降低到原来的水平。尤其是在Windows和Macintosh的应用越来越复杂的情况
下,更是如此。
以上就是我最终决定写这本书的原因
Microsoft出版社已经同意出版这本书。
情况就是这样。
我希望您会喜欢这本书,我力图使本书不那么枯燥并尽量有趣。

Steve Maguire
西雅图,华盛顿
1992.10.22



致谢

我要感谢Microsoft出版社中帮助本书问世的所有人,尤其是在我写作过程中始终手把手地
教我的两个人。首先我要感谢我的约稿编辑Mike Halvorson,他使我能够按照自己的进度完
成了这本书,并且耐心地解答了我这个新作者提出的许多问题。我还要特别感谢我的责任编
辑Erin O’Connor女士,她用了许多额外时间及早地对我写出的章节提出了反馈意见。没有
他们的帮助,就不会有这本书。Erin还鼓励我以幽默的风格写这本书。她对文中的小俏皮话
发笑当然不会使我不快。
我还要感谢我的父亲Joseph Maguire是他在70年代中期把我引入早期的微机世界:Altair、
IMSAI和Sol-20,使我与这一行结缘。我要感谢我于1981~83年在Valpar International工
作时的伙伴Evan Rosen,他是对我职业生涯最有影响的几个人之一,他的知识以及洞察力在
本书中都有所体现。还有Paul Davis,在过去的10年里,我与他在全国各地的许多项目中都
有过愉快的合作,他也无疑的塑造了我的思维方式。
最后感谢花时间阅读本书草稿,并提供技术反馈意见的所有人。他们是:Mark Gerber、
Melissa Glerum、Chris Mason、Dave Moore、John Rae-Grant和Alex Tilles。特别感谢
Eric Schlegel和Paul Davis,他们不仅是本书草稿的评审者,而且在本书内容的构思上对
我很有帮助。



命名约定
本书采用的命名约定和Microsoft所使用的“匈牙利式”命名约定差不多。该约定是由生于
匈牙利布达佩斯的Charles Simonyi开发的,它通过在数据和函数名中加入额外的信息以增
进程序员对程序的理解。例如:
char ch;            /* 所有的字符变量均以ch开始    */
byte b;             /* 所有的字节均冠以b    */
long l;             /* 所有的长字均冠以l    */
对于指向某个数据类型的指针,可以先象上面那样建立一个有类型的名字,然后给该名字加
上前缀字母P:
char* pch;          /* 指向ch的指针以p开始 */
byte* pb;           /* 同理 */
long* pl;
void* pv;           /* 特意显用的空指针 */
char** ppch;        /* 指向字符指针的指针 */
byte** ppb;         /* 指向字节指针的指针 */
匈牙利式名字通常不那么好念,但在代码中读到它们时,确实可以从中得到许多的信息。例
如,当你眼看到某个函数里有一个名为pch的变量时,不用查看声明就立即知道它是一个指
向字符的指针。
为了使匈牙利式名字的描述性更强.或者要区分两个变量名,可以在相应类型派生出的基本
名字之后加上一个以大写字母开头的“标签”。例如,strcpy函数有两个字符指针参数:一
个是源指针,另一个是目的指针。使用匈牙利式命名约定,其相应的原型是:
char* strcpy(char* pchTo, char* pchFrom);       /* 原型 */
在上面的例子中,两个字符指针有一个共同的特点 ── 都指向以0为结尾的C的字符串。因
此在本书中,每当用字符指针指向字符串时,我们就用一个更有意义的名子str来表示。因
此,上述strcpy的原型则为:
char* strcpy(char* strTo, char* strFrom)            /* 原型 */
本书用到另一个类型是ANSI标准中的类型size_t。下面给出该类型的一些典型用法:
size_t sizeNew, sizeOld;                            /* 原型 */
void* malloc(size_t size);                          /* 原型 */
void* realloc(void* pv, size_t sizeNew);            /* 原型 */
函数和数组的命名遵循同样的约定,名字由相应的返回类型名开始,后跟一个描述的标签。
例如:
ch = chLastKeyPressed;                  /* 由变量得一字符 */
ch = chInputBuffer[];                   /* 由数组得一字符 */
ch = chReadKeyboard;                    /* 由函数得一字符 */
如果利用匈牙利式命名方法,mall~和reali~可以写成如下形式:
void* pvNewBlock(size_t size);                      /* 原型 */
void* pvResizeBlock(void* pv, size_t sizeNew);      /* 原型 */
由于匈牙利式命名方法旨在增进程序员对程序的理解,所以大多数匈牙利式名字的长度都要
超过ANSI严格规定6个字母的限制。这就不妙,除非所用的系统是几十年前设计的系统,否
则这6个字母的限制只当是历史的遗迹。
以上内容基本上没有涉及到匈牙利式命名约定的细节,所介绍的都是读者理解本书中所用变
量和函数名称意义的必需内容。如果读者对匈牙利式命名约定的详细内容感兴趣,可以参考
本书末尾参考文献部分列出的Simonyi的博士论文。

某些背景

本书用到了一些读者可能不太熟悉的软件和硬件系统的名称。下面对其中最常见的几个系统
给出简单的描述
Macintosh          Macintosh是Apple公司的图形窗口计算机,公布于1984年。它是最先
支持“所见即所得”拥户界面的流行最广的计算机。
Windows            Windows是Microsoft公司的图形窗口操作系统。Microsoft公司1990年
公布了Windows 3.0,该版本明显好于其早期版本。
Excel              Excel是Microsoft公司的图形电子表格软件,1985年首次在
Macintosh上公布,随后在进行了大量的重写和排错工作后,被移植到Windows上。多年来
Macintosh Excel和Windows Excel共用一个名字,但程序所用的代码并不相同。
在本书中,找多次提到曾经当过Macintosh Excel程序员这一经历。但应该说明的是,我的
大部分工作是将Windows Excel的代码移到Macintosh Excel上或者是实现与Windows Excel
相似的特征。我与该产品现在的惊人成功并没有特别的关系。
我为Macintosh Excel所做的唯一真正重要的贡献是说服Microsft放弃掉Macintosh Excel,
而直接利用Windows Excel的源代码构造Macintosh的产品。Macintosh 2.2版是第一个基于
Windows Excel的版本,它享用了Windows Excel 80%的源代码。这对Macintosh Excel的用
户意义重大,因为用了2.2版以后他们会感至Macintosh Excel的功能和质量都有了一个很大
的飞跃。
Word               Word是Microsoft公司的字处理应用软件。实际上,Word有三种版本:
基于字符并在MS-DOS上运行的 DOS版;Macintosh版;Windows版。到目前为止,这三种版
本的产品虽然是用不同的源代码做出的,但它们非常相象,用户改用任何一个都不会有什么
困难。由于Excel利用共享代码获得了成功,Microsoft已经决定Word的高版本也将采用共享
代码进行开发。
80x86           80x86是MS-DOS和Windows机器常用的Intel CPU系列。
680x0           680x0是各种Macintosh所用的Motorola CPU系列。


引言

几年前在一次偶然翻阅Donald Knuth所著《TEX:The Program》一书时,序言中的一段话深
深触动了我:

我确信TEX的最后一个错误已经在1985年11月27日被发现并排除掉了。但是如果出于目前尚
不知道的原因,TEX仍然潜伏有错误,我非常愿意付给第一个发现者$20.48元。(这一金额
已是以前的两倍。我打算在本年内再增加一倍。你看我是多么自信!)

我对Knuth是否曾经付给某人$20.48甚至$40.96元不感兴趣,这并不重要。重要的是他对他
的程序所具有的那种自信。那么据你所知,究竟有多少程序员会严肃地声称他们的程序完全
没有错误?又有多少敢把这一声称印刷在书上,并准备为错误的发现者付钱呢?
如果程序员确信测试组已经发现了所有的错误,那么他也许敢作这种声明。但这本身就是一
个问题。每当代码被打包装送给程序经销商之前,人们在胸前划着十字带着最好的愿望说:
“希望测试已经发现了所有的错误”。这一情景我已见过多次了。
由于现代的程序员已经放弃了对代码进行彻底测试的职责,他们没法知道代码中是否有错。
管理人员也不会公布测试情况,只是说:“别操那个心,测试人员会为你作好测试的”。更
为微妙的是,管理人员希望程序员自己进行代码的测试。同时,他们希望测试员作得更彻底
些,因为毕竟这是他们的本职工作。
正如你在本书中将会看到的那样,编写无错代码的技术或许有几百种,程序员能用,但测试
人员却无法使用,因为这些技术和代码的编写直接相关。

两个关键的问题

本书介绍的所有决窍是当发现错误时,不断地就以下两个问题追问自己的结果:
l         怎样才能自动地查出这个错误?
l         怎样才能避免这个错误?
第一个问题可能使读者认为本书是有关测试的书,其实不是。当编辑程序发现语法错误时,
它是在做测试吗?不,不是。编辑程序只是在自动地检查代码中的错误。语法错误只是程序
员可以使用的自动查错方法查出的一种最基本的错误类型。本书将详尽介绍自动向程序员提
示错误的方法。
编写无错代码的最好方法是把防上错误放在第一位。关于这个问题,同样也有许多的技巧。
某些技巧与常用的编码惯例有关,但它们不是象“每个人都违背原则”或“没有人违背该原
则”这样泛泛地考虑问题,而是对相应的细节进行详细的讨论。要记住,在任何时候跟在大
多数人的后面常常是所能选择的最坏一条路。因此在成为别人的追随者之前一定要确定这样
做确实有意义,而且不要仅仅因为其它的什么人如此自己也如此。
本书的最后一章讨论编写无错代码应持正确态度的重要性。如果没有正确的态度,发现错误
和防止错误就好比在冬季大开着窗户给房间加热,虽然也能达到目的,但要浪费大量的能量

本书除第4章和第8章以外都配有练习。但要注意,这些练习并不是要测验读者对相应内容的
理解。实际上更多的是作者想在该章的正文中阐述却没能放进去的要点。其它的练习为的是
让读者思考与该章内容有关的一些问题,打开思路,琢磨一下以前未曾考虑过的概念。
无论哪种情况,都是想通过练习再补充一些新的技巧和信息,因此值得一读。为使读者了解
作者的意图,本书在附录C中提供了所有练习的答案。大部分章节还给出了一些课题,但这
些课题没有答案,因为这种课题通常是任务,而不是问题。

规则或者建议

本书的编排类似于Brian Kernighan和P. J. Plauger所写的程序设计经典著作《The
Elements of Programming Sytle》。该书出于William Strunk Jr.和E. B. White所写的重
要经典著作《The Elements of Style》。这两本书采用同样的基本概念表达方法:
l         给出一个例子;
l         指出该例子中的某些问题所在;
l         用一般的准则改进该例子。
确实,这是个程式,而且是使读者感到舒服的程式,因此本书同样采用了这一程式。作者力
图使本书读起来是一种享受,尽管它有着公式的性质。希望读者会觉得本书很有趣。
本书还给出一些似乎不应违背的“一般准则”。我们的第一条准则是:
每条准则都有例外


由于准则是用来说明一般情况的,所以本书一般并不指明准则的例外情况,而把它留给读者
。我相信,当读者读到某个准则时,肯定会怀疑道:“噢,当……时,不是这样的……”。
如果某个人对你说:“不能闯红灯”,虽然这是一条准则,但你肯定能够举出一种特殊情况
,在这种情况下闯红灯倒是个正确的行动。这里关键是要记住准则只是在一般情况下才有意
义,因此只有理由十分充足时,才可以违背准则。

关于本书代码的说明

本书的所有代码都是按ANSI C写的,并且通过了MS-DOS、Microsoft Windows和Apple
Macintosh上五个流行编译程序的测试:
Microsoft C/C++ 7.0             Microsoft公司
Turbo C/C++ 3.0                 Borland国际公司
Aztec 5.2                       Manx软件系统公司
MPW C 3.2                       Apple计算机公司
THINK C 5.0                     Symantec公司
还有一个问题:如果读者想从本书中摘取代码用在自己的程序中,那要小心。因为为了说明
书中的论点,许多例子都有错误。另外,书中用到的函数虽然名字和功能都与ANSI C的标准
库函数相同,但已对相应的界面进行了一些小的修改。例如ANSI版memchr函数的界面是:
void* memchr(const void* s, int c, size_t n);
这里memchr的内部将整数c当作unsigned char来处理。在本书的许多地方,读者都会看到字
符类型被显式地声明为unsigned char,而不是int:
void* memchr(const void* pv, unsigned char ch, size_t size);
ANSI标准将所有的字符变元都声明为int,是为了保证其库函数同样可以用于ANSI标准之前
编写的非原型程序,这时程序使用extern声明函数。由于在本书中只使用ANSI C,所以不必
考虑这些向下兼容的细节而可以用更加精确的类型声明以改进程序的清晰程度并用原型进行
强类型检查(详见第1章)。

“提到Macintosh了吗?”

出于某种原因,一本书如果没有提到 PDP11,Honeywell 6000,当然还有 IBM 360,就不会
被认真对待。因此,我在这里也提到了它们。仅此而已,读者在本书中再也不会见到这些字
眼、读者见到最多的是MS-DOS,Microsoft Windows,特别还有Apple Macintosh,因为近年
来我一直为这些系统编写代码。但是应该注意,本书中的任何代码都不受这些特定的系统约
束。它们都是用通用的C编写的,应该能够工作于任何的ANSI C编译程序下。因此即使读者
使用的系统本书没有提及,也不必担心这些操作系统的细节会产生障碍。
应该提到的是在大多数的微机系统中,用户都可以通过NULL指针进行读写,破坏栈框架并在
内存甚至是其它应用程序的内存区中留下许多的无用信息,而硬件并没有什么反应,听任用
户为所欲为。之所以提到这一点,是因为如果读者习惯于认为通过NULL指针进行写操作会引
起硬件故障的话,那么可能会对本书中的某些语句感到迷惑不解。遗憾的是,目前微机上的
保护型操作系统仍不普及,破坏内存的隐患必须通过硬件保护(通常它也不能提供充足的保
护)之外的方法才能发现。

有错误就有错误

不必为本书的读者定义什么叫错误,相信读者都知道什么是错误。但是错误可以分为两类:
一类是开发某一功能时产生的错误,另一类是在程序员认为该功能已经开发完成之后仍然遗
留在代码中的错误。
例如在Microsoft中,每个产品都由一些绝不应该含有错误的原版源代码构成。当程序员给
产品增加新功能时,并不直接改变相应的原版源代码,改变的是其副本。只有在这些改变已
经完成,并且程序员确信相应代码中已经没有错误时,才将其合并到原版源代码中。因此从
产品质量看,在实现指定功能过程中不论产生多少个错误都没有关系,只要这些错误在相应
代码被并入原版源代码之前被删除掉就行。
所有的错误都有害,但损害产品最危险的错误是已经进入原版源代码中的错误。因此,本书
中提到的错误指的就是这些已经进入原版源代码中的错误。作者并不指望程序员在键入计算
机之前总是写出没有错误的代码,但确信防止错误侵入原版源代码是完全可能的。尤其是程
序员在使用了本书提供的秘诀之后,更是如此。


--

※ 来源:·荔园晨风BBS站 bbs.szu.edu.cn·[FROM: 192.168.111.118]


[回到开始] [上一篇][下一篇]

荔园在线首页 友情链接:深圳大学 深大招生 荔园晨风BBS S-Term软件 网络书店