设为首页 加入收藏

TOP

C 编程最佳实践(三)
2011-03-18 13:11:05 】 浏览:3591
Tags:编程 最佳 实践
是否相等,决不应该以缺省方式处理它的结果。更可取的方法是定义宏 STREQ:

#define STREQ(str1, str2) (strcmp((str1), (str2)) == 0)

用这种方法,语句

If ( STREQ( inputstring, somestring ) ) ...

就具有隐含的行为,该行为不大会在您不知情的情况下改变(人们往往不会重新编写或重新定义象 strcmp()这样的标准库函数)。

不要用 1 检查相等性的布尔值(TRUE 和 YES 等);而要用 0 测试不等性(FALSE 和 NO 等)。绝大多数函数被确保在条件为假(false)时返回 0,但仅在条件为真(true)时才返回非零。因此,最好将

if (func() == TRUE) {...

写成

if (func() != FALSE)

  • 嵌入语句

    使用嵌入赋值语句要看时间和地点。在有些构造中,如果不使用更多且不易阅读的代码就没有更好的方法来实现结果:

    while ((c = getchar()) != EOF) { process the character }

  • 使用嵌入赋值语句来提高运行时性能是可能的。但是,您应当在提高速度和降低可维护性之间加以权衡,在人为指定的位置使用嵌入赋值语句会导致可维护性降低。例如:

    x = y + z; d = x + r;

    不应被替换为:

    d = (x = y + z) + r;

    即使后者可能节省一个周期也不行。最终,这两者之间在运行时间上的差异将随着优化器的增强而减少,易维护性的差异却将增加。

  • goto 语句

    应保守地使用 goto。从数层 switch、 for和 while嵌套中跳出来时,使用该语句很有效,不过,如果有这样的需要,则表明应将内部构造分解成单独的函数。

    for (...) { while (...) { ... if (wrong) goto error; } } ... 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返回的内存进行的初始化。

    • 使您程序的目的和结构清晰。
    • 要记住,可能会在以后要求您或别的人修改您的代码或在别的机器上运行它。细心编写您的代码,以便能够将它移植到其它机器。


    结束语

    应用程序的维护要花去程序员的大量时间,这是众所周知的事。部分原

  • 首页 上一页 1 2 3 4 下一页 尾页 3/4/4
    】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
    上一篇如何在linux下检测内存泄漏 下一篇Linux 下几个文件操作命令的代码..

    最新文章

    热门文章

    Hot 文章

    Python

    C 语言

    C++基础

    大数据基础

    linux编程基础

    C/C++面试题目