C 编程最佳实践

来源:互联网 发布:苹果手机应用数据删除 编辑:程序博客网 时间:2024/04/30 04:05
  }
    ...
            error:
          print a message

当必须使用 goto 时,随附的标号应单独位于一行,并且同后续代码的左边相距一个制表符或位于一行的开头。对 goto 语句和目标都应加上注释,说明其作用和目的。
switch 中的“落空”(fall-through)

当一块代码有数个标号时,将这些标号放在单独的行。这种风格与垂直空格的使用一致,并且使重新安排 case 选项(如果那是必需的话)成了一项简单的任务。应对 C switch 语句的“落空”特征加以注释,以便于以后的维护。如果这一特性曾给您带来“麻烦”,那么您就能够理解这样做的重要性!

switch (expr) { 
case ABC:
case DEF:
statement;
break;
case UVW:
statement;/*FALLTHROUGH*/
case XYZ:
statement;
break;
}


尽管从技术上说,最后一个 break 不是必需的,但是,如果以后要在最后一个 case 之后添加了另一个 case,那么一致地使用 break 可以防止“落空”错误。如果使用 default case 语句的话, 它应当永远是最后一个,并且(如果它是最后的语句)不需要最后的 break 语句。
常量

符号常量使代码更易于阅读。应尽量避免使用数字常量;使用 C 预处理器的 #define 函数给常量赋予一个有意义的名称。在一个位置(最好在头文件中)定义值还会使得管理大型程序变得更容易,因为只需更改定义就可以统一地更改常量值。可以考虑使用枚举数据类型作为对声明只取一组离散值的变量的改进方法。使用枚举还可以让编译器对您枚举类型的任何误用发出警告。任何直接编码的数字常量必须至少有一个说明值的出处的注释。

常量的定义与它的使用应该一致;例如,将 540.0 用于浮点数,而不要通过隐式浮点类型强制转换使用 540。也就是说,在有些情况下,常量 0 和 1 可以以本身的形式直接出现,而不要以定义的形式出现。例如,如果某个 for 循环遍历一个数组,那么:

for (i = 0; i < arraysub; i++)


非常合理,而代码:

gate_t *front_gate = opens(gate[i], 7); 
if (front_gate == 0)
error("can't open %s/n", gate[i]);


就不合理。在第二个示例中,front_gate 是指针;当值是指针时,它应与 NULL 比较而不与 0 比较。即使象 1 或 0 这样的简单值,通常最好也使用象 TRUE 和 FALSE 这样的定义来表示(有时 YES 和 NO 读起来更清楚)。

不要在需要离散值的地方使用浮点变量。这是由于浮点数不精确的表示决定的(请参阅以上 scanf 中的第二个测试)。使用 <= 或 >= 测试浮点数;精确比较(== 或 !=)也许不能检测出“可接受的”等同性。

应将简单的字符常量定义为字符文字而不是数字。不提倡使用非文本字符,因为它们是不可移植的。如果必须使用非文本字符,尤其是在字符串中使用它们,则应使用三位八进制数(不是一个字符)的转义字符(例如“/007”)来编写它们。即便如此,这样的用法应视为与机器相关,并且应按这一情况来处理。
条件编译

条件编译可用于机器相关性、调试以及在编译时设置某些选项。可以用无法预料的方式轻易地组合各种控制。如果将 #ifdef 用于机器相关性,应确保当没有指定机器时会出错,而不是使用缺省的机器。#error 伪指令可以较方便地用于这一用途。如果使用 #ifdef 进行优化,缺省值应是未优化的代码而不是不可编译或不正确的程序。要确保对未优化的代码进行了测试。

其它
象Make 这样用于编译和链接的实用程序极大简化了将应用程序从一个环境移到另一个环境的任务。在开发期间, make 仅对那些自上次使用 make 以来发生了更改的模块进行重新编译。

经常使用 lint 。lint 是 C 程序检查器,它检查 C 源文件以检测并报告函数定义和调用之间类型的不匹配和不一致,以及可能存在的程序错误等。

此外,研究一下编译器文档,了解那些使编译器变得“吹毛求疵”的开关。编译器的工作是力求精确,因此通过使用适当的命令行选项让它报告可能存在的错误。
使应用程序中全局符号的数量最少。这样做的好处之一是与系统定义的函数冲突的可能性降低。
许多程序在遗漏输入时会失败。对所有的程序都应进行空输入测试。这也可能帮助您理解程序的工作原理。
不要对您的用户或您所用的语言实现有任何过多的假设。那些“不可能发生”的事情有时的确会发生。健壮的程序可以防范这样的情形。如果需要找到某个边界条件,您的用户将以某种方式找到它!

永远不要对给定类型的大小作任何假设,尤其是指针。

当在表达式中使用 char 类型时,大多数实现将它们当作无符号类型,但有些实现把它们作为有符号的类型。当在算术表达式使用它们时,建议始终对它们进行类型强制转换。

不要依靠对自动变量和 malloc 返回的内存进行的初始化。
使您程序的目的和结构清晰。
要记住,可能会在以后要求您或别的人修改您的代码或在别的机器上运行它。细心编写您的代码,以便能够将它移植到其它机器。

结束语
应用程序的维护要花去程序员的大量时间,这是众所周知的事。部分原因是由于在开发应用程序时,使用了不可移植和非标准的特性,以及不令人满意的编程风格。在本文中,我们介绍了一些指南,多年来它们一直给予我们很大帮助。我们相信,只要遵守这些指南,将可以使应用程序维护在团队环境中变得更容易。

参考资料
Obfuscated C and Other Mysteries,由 Don Libes 编写,John Wiley and Sons, Inc. ISBN 0-471-57805-3
The C Programming Language,Second Edition,由 Brian W. Kernighan 和 Dennis M. Ritchie 撰写,Prentice-Hall,ISBN 0-13-110370-9
Safer C,由 Les Hatton 编写,McGraw-Hill,ISBN 0-07-707640-0
C Traps and Pitfalls 由 Andrew Koenig 编写,AT&T Bell Laboratories,ISBN 0-201-17928-9

关于作者
Shiv Dutta 是 IBM Systems Group 的一名技术顾问,他帮助独立软件供应商在 pSeries 服务器启用他们的应用程序。Shiv 有作为软件开发人员、系统管理员和讲师的丰富经验。他在 AIX 的系统管理、问题确定、性能调优和规模指导方面提供支持。Shiv 在 AIX 诞生之时就从事这方面的工作。他从 Ohio University 获得物理博士学位,可以通过 sdutta@us.ibm.com 与他联系。

Gary R. Hook 是 IBM 的高级技术顾问,为独立软件供应商提供应用程序开发、移植和技术援助。Hook 先生的职业经历主要在基于 Unix 的应用程序开发方面。在 1990 年加入 IBM 时,他在位于得克萨斯州 Southlake 的 AIX Technical Support 中心工作,为客户提供咨询和技术支持服务,重点在 AIX 应用程序体系结构方面。Hook 先生现在居住在奥斯汀,在 1995 到 2000 年期间,他是 AIX Kernel Development 团队的一员,专门研究 AIX 链接程序、装入程序和通用应用程序开发工具。可以通过 ghook@us.ibm.com 与他联系。