Microsoft编写优质无错C程序秘诀 - 摘录
Posted on 2004年10月20日 1:26 - from my blog :D
*
消除程序错误的最好方法是尽可能早、尽可能容易地发现错误,要寻求费力最小的自动查错方法。
*
努力减少程序员查错所需的技巧。可以选择的编译程序或lint警告设施并不要求程序员要有什么查错的技巧。在另一个极端,高级的编码方法虽然可以查出或减少错误,但它们也要求程序员要有较多的技巧,因为程序员必须学习这些高级的编码方法。
*
考查所编写的子系统,问自己:“在什么样的情况下,程序员在使用这些子系统时会犯错误。”在子系统中加上相应的断言和确认检查代码,以捕捉难于发现的错误和常见的错误。
*
如果不能使错误不断重现,就无法排除它们。找出程序中可能引起随机行为的因素,并将它们从程序的调试版本中清除。把目前尚“无定义”的内存单元置成了某个常量值,就可能产生这种错误。在这种情况下,如果程序在该单元被正确地定义为某个值之前引用了它的内容,那么每次执行这部分错误的代码,都会得到同样的错误结果。
*
如果所编写的子系统释放内存(或者其它的资源),并因此产生了“无用信息”,那么要把它搅乱,使它真的象无用信息。否则,这些被释放了的数据就有可能仍被使用,而又不会被注意到。
*
类似地,如果在所编写的子系统中某些事情可能发生,那么要为该子系统加上相应的调试代码,使这些事情一定发生。这样可以增大查出通常得不到执行的代码中的错误的可能性。
*
尽力使所编写的测试代码甚至在程序员对其没有感觉的情况下亦能起作用。最好的测试代码是不用知道其存在也能起作用的测试代码。
*
如果可能的话,把测试代码放到所编写的子系统中,而不要把它放到所编写子系统的外层。不要等到进行了系统编码时,才考虑其确认方法。在子系统设计的每一步,都要考虑“如何对这一实现进行详尽的确认”这一问题。如果发现这一设计难于测试或者不可能对其进行测试,那么要认真地考虑另一种不同的设计,即使这意味着用大小或速度作代价去换取该系统的测试能力也要这么做。
*
在由于速度太慢或者占用的内存太多而抛弃一个确认测试程序之前,要三思而后行。切记,这些代码并不是存在于程序的交付版本中。如果发现自己正在想:“这个测试程序太慢、太大了”,那么要马上停下来问自己:“怎样才能保留这个测试程序,并使它既快又小?”
*
代码中不会自己生出错误来,错误是程序员编写新代码或者修改现有代码的产物。
*
如果你想发现代码中的错误,没有哪个办法比在对代码进行编译时对其进行逐条跟踪更好。
*
虽然直观上你可能认为对代码进行走查会花费大量的时间,但这是不对的。刚开始进行代码的走查确实要多花一点时间,但当这一切习惯成自然之后并不会多花多少时间,你可以很快地走查一遍。
*
一定要对每一条代码路径进行逐条的跟踪,至少要跟踪一遍,尤其是对代码中的错误处理部分。不要忘记 &&、|| 和?:这些运算符,它们每个都有两条代码路径需要进行测试。
*
在某些情况下也许需要在汇编语言级对代码进行逐条的跟踪。尽管不必经常这样做,但在必要的时候不要回避这种做法。
*
最容易使用和理解的函数界面,是其中每个输入和输出参数都只代表一种类型数据的界面。把错误值和其它的专用值混在函数的输入和输出参数中,只会搞乱函数的界面。
*
设计函数的界面迫使程序员考虑所有重要细节(如错误情况的处理),不要使程序员能够很容易地忽视或者忘记有关的细节。
*
老要想到程序员调用所编函数的方式,找出可能使程序员无意间引入错误的界面缺陷。尤其重要的是要争取编出永远成功的函数,使调用者不必进行相应的错误处理。
*
编写注解突出可能的异常情况
*
为了增加程序的可理解性从而减少错误,要保证所编函数的调用能够被必须阅读这些调用的程序员所理解。莫明其妙的数字和布尔参数都与这一目标背道而驰,因此应该予以消除。
*
分解多功能的函数。取更专门的函数名(如ShrinkMemory而不是 realloc)不仅可以增进人们对程序的理解,而且使我们可以采用更加严格的断言自动地检查出调用错误。
*
为了向程序员展示出所编函数的适当调用方法,要在函数的界面中通过注解的方式详细说明。
*
要强调危险的方面。
*
在选择数据类型的时候要谨慎。虽然ANSI标准要求所有的执行程序都要支持char,int,long等类型,但是它并没有具体定义这些类型。为了避免程序出错,应该只按照ANSI的标准选择数据类型。
*
由于代码可能会在不理想的硬件上运行,因此很可能算法是正确的而执行起来却有错。所以要经常详细检查计算结果和测试结果的数据类型范围是否上溢或下溢。
*
在实现某个设计的时候,一定要严格按照设计去实现。如果在编写代码时只是近似地实现所提出的要求,那就很容易出错。
*
每个函数应该只有一个严格定义的任务,不仅如此,完成每个任务也应只有一种途径。假如不管输入什么都能执行同样的代码,那就会大大降低那些不易被发现的错误所存在的概率。
*
if语句是个警告信号,说明代码所做的工作可能比所需要的要多。努力消除代码中每一个不必要的if语句,经常反问自己:“怎样改变设计从而删掉这个特殊情况?”有时可能要改变数据结构,有时又要改变一下考察问题的方式,就象透镜是凸的还是凹的问题一样。
*
有时if语句隐藏在while和for循环的控制表达式中。“?:”操作符是if语句的另外一种形式。
*
曾惕有风险的语言惯用语,注意那些相近但更安全的惯用语。特别要警惕那些看上去象是好编码的惯用语,因为这样的实现对总体效率很少有显著的影响,但却增加了额外的风险性。
*
在写表达式时,尽量不要把不同类型的操作符混合起来,如果必须混合使用,用括号把它们分隔开来。
*
特殊情况中的特殊情况是错误处理。如果有可能,应该尽量避免调用可能失败的函数,假如必须调用返回错误的函利,将错误处理局部化以便所有的错误都汇集到一点,这将增加在错误处理代码中发现错误的机会。
*
在某些情况下,取消一般的错误处理代码是有可能的,但要保证所做的事情不会失败。这就意味着在初始化时要对错误进行一次性处理或是从根本上改变设计。
*
如果你要用到的数据不是你自己所有的,那怕是临时的,也不要对其执行写操作。尽管你可能认为读数据总是安全的,但是要记住,从映射到I/O的存储区读数据,可能会对硬件造成危害。
*
每当释放了存储区人们还想引用它,但是要克制自己这么做。引用自由存储区极易引起错误。
*
为了提高效率,向全局缓冲区或静态缓冲传递数据也是很吸引人的,但是这是一条充满风险的捷径。假若你写了一个函数,用来创建只给调用函数使用的数据,那么就将数据返回给调用函数,或保证不意外地更改这个数据。
*
不要编写依赖支持函数的某个特殊实现的函数。我们已经看到,FILL例程不该象给出的那样调用CMOVE,这种写法只能作为坏程序设计的例子。
*
在进行程序设计的时候,要按照程序设计语言原来的本意清楚、准确地编写代码。避免使用有疑问的程序设计惯用语,即使语言标准恰好能保证它工作,也不要使用。请记住,标准也在改变。
*
如果能用C语言有效地表示某个概念,那么类似地,相应的机器代码也应该是有效的。逻辑上讲似乎应该是这样,可是事实上并非如此。因此在你将多行C代码压缩为一行代码之前,一定要弄清楚经过这样的更改以后,能否保证得到更好的机器代码。
*
最后,不要象律师写合同那样来编写代码。如果一般水平的程序员不能阅读和理解你的代码,那就说明你的代码太复杂了,使用简单一点的语言。
*
错误既不会自己产生,也不会自己改正。如果你得到了一个错误报告,但这个错误不再出现了。不要假设测试员发生了幻觉,而要努力查找错误,甚至要恢复程序的老版本。
*
不能“以后”再修改错误。这是许多产品被取消的共同教训。如果在你发现错误的时候就及时地更正了错误,那你的项目就不会遭受毁灭性的命运。当你的项目总是保持近似于0个错误时,怎么可能会有一系列的错误呢?
*
当你跟踪查到一个错误时,总要问一下自己,这个错误是否会是一个大错误的症状。当然,修改一个刚刚追踪到的症状很容易,但是要努力找到真正的起因。
*
不要编写没有必要的代码。让你的竞争者去清理代码,去实现“冷门”但无价值的特征,去实现自由特征。让他们花大量的时间去修改由于这些无用代码所引起的所有没有必要的错误。
*
记住灵活与容易使用并不是一回事。在你设计函数和特征时,重点是使之容易使用;如果它们仅仅是灵活的,象realloc函数和Excel中的彩色格式特征那样,那么就没法使得代码更加有用;相反地,使得发现错误变得更困难了。
*
不要受“试一试”某个方案以达到预期结果的影响。相反,应把花在尝试方案上的时间用来寻找正确的解决方法。如果必要,与负责你操作系统的公司联系,这比提出一个在将来可能会出问题的古怪实现要好。
*
代码写得尽量小以便于全面测试。在测试中不要马虎。记住,如果你不测试你的代码,就没有人会测试你的代码了。无论怎样,你也不要期望测试组为你测试代码。
*
最后,确定你们小组的优先级顺序,并且遵循这个顺序。如果你是约克,而项目需要吉尔,那么至少在工作方面你必须改变习惯。
问题点数:0、回复次数:18Top
1 楼imRainman(雨人)回复于 2004-12-04 15:35:08 得分 0
沙发,顶一下!Top
2 楼jk_one(子曰诗云)回复于 2004-12-04 17:26:21 得分 0
顶!!!Top
3 楼AnavelGato()回复于 2004-12-04 18:22:22 得分 0
UP!!!!!!!Top
4 楼asimpleman(simple_man)回复于 2004-12-04 23:21:59 得分 0
gTop
5 楼csbird()回复于 2004-12-05 12:16:59 得分 0
dingTop
6 楼dongyuanzhang(阿林)回复于 2004-12-05 12:24:30 得分 0
还行!Top
7 楼sharkhuang(走吧走吧!人总会慢慢长大~)回复于 2004-12-05 15:21:49 得分 0
执行起来很难啊Top
8 楼coyprightbao(Mr'Bao)回复于 2004-12-06 21:25:54 得分 0
mark!Top
9 楼lovessm(绅士亦花心)(gentlelover)回复于 2004-12-08 19:32:25 得分 0
mark.Top
10 楼avalonBBS("︶.︶メ)→( ̄ε ̄メ)回复于 2004-12-09 09:47:01 得分 0
呵呵,顶一下
Top
11 楼lqgoal(晴天)回复于 2004-12-09 10:12:35 得分 0
说的很模糊Top
12 楼goodluckyxl(被人遗忘的狗)回复于 2004-12-09 10:32:59 得分 0
确实不太直观
一般的注意点最好配上相应的DEMO
看起来一目了然
还是支持一下Top
13 楼qiusong1983(冰冻的火)回复于 2004-12-09 11:41:21 得分 0
*
错误既不会自己产生,也不会自己改正。如果你得到了一个错误报告,但这个错误不再出现了。不要假设测试员发生了幻觉,而要努力查找错误,甚至要恢复程序的老版本。
*
只是为了这条也值得顶
虽然操作起来满难
Top
14 楼coyprightbao(Mr'Bao)回复于 2004-12-09 13:03:51 得分 0
mark!Top
15 楼yelang771(牧野流星)回复于 2004-12-09 13:11:42 得分 0
upTop
16 楼stevens2009(风)回复于 2004-12-09 13:32:38 得分 0
markTop
17 楼jackyhubin(想吃三明治)回复于 2004-12-09 14:33:18 得分 0
好长,没看完。
觉得把好的编程习惯保持,去的不好的不就OK了吗?Top
18 楼yangfasheng(悟法:前面是绝路,希望在拐角)回复于 2004-12-11 12:47:40 得分 0
UPTop




