C 编程最佳实践--IBMdeveloperWorks

上传人:woxinch****an2018 文档编号:38980260 上传时间:2018-05-10 格式:DOC 页数:12 大小:122.50KB
返回 下载 相关 举报
C 编程最佳实践--IBMdeveloperWorks_第1页
第1页 / 共12页
C 编程最佳实践--IBMdeveloperWorks_第2页
第2页 / 共12页
C 编程最佳实践--IBMdeveloperWorks_第3页
第3页 / 共12页
C 编程最佳实践--IBMdeveloperWorks_第4页
第4页 / 共12页
C 编程最佳实践--IBMdeveloperWorks_第5页
第5页 / 共12页
点击查看更多>>
资源描述

《C 编程最佳实践--IBMdeveloperWorks》由会员分享,可在线阅读,更多相关《C 编程最佳实践--IBMdeveloperWorks(12页珍藏版)》请在金锄头文库上搜索。

1、C 编程最佳实践Shiv Dutta (), 技术顾问, IBM Gary R. Hook (), 高级技术顾问, IBM 简介:简介: 尽管 C 语言问世已近 30 年,但它的魅力仍未减退。C 语言继续吸引着众多的人 们,他们为了编写新的应用程序,或者移植或维护现有的应用程序而必须学习新技能。 标记本文! 发布日期:发布日期: 2004 年 4 月 05 日 (最初发布 2003 年 9 月 09 日) 级别:级别: 初级 访问情况访问情况 832 次浏览 建议建议: 0 (添加评论)平均分 (共 1 个评分 )简介简介本文是为了满足开发人员的需要而写的。我们总结了一套指南,无论作为开发人员

2、还是顾 问,这些指南多年来一直都很好地指导着我们,我们把它们作为建议提供给您,希望对您 的工作有所帮助。您也许不赞同其中的某些指南,但我们希望您会喜欢其中的一些并在您 的编程或移植项目中使用它们。 回页首回页首风格与指南风格与指南使用一种使代码具有可读性和一致性的源代码风格。如果没有团队代码风格或自己 的风格,您可以使用与大多数 C 程序员采用的 Kernighan 和 Ritchie 风格相似 的风格。然而,举一个极端的例子,有可能最终会写出与下面相似的代码:int i;main()for(;i“next=NULL 注释注释 注释应描述正在发生什么事、如何完成它、参数表示什么、使用了哪些全局

3、变量以 及任何限制或错误。但要避免不必要的注释。如果代码比较清晰,并且使用了良好 的变量名,那么它应该能够较好地说明自身。因为编译器不检查注释,所以不保证 它们是正确的。与代码不一致的注释会起到相反的作用。过多的注释会使代码混乱。下面是一种多余的注释风格:i=i+1; /* Add one to i */很明显变量 i 递增了 1。还有更糟的注释方法:/* * Add one to i * */i=i+1;命名约定命名约定 具有前导和尾随下划线的名称是为系统用途而保留的,不应当用于任何用户创建的 名称。约定规定: 1.#define 常量应全部大写。 2.enum 常量应以大写字母开头或全部大

4、写。3.函数、类型定义(typedef)和变量名以及结构(struct)、联合(union) 和枚举(enum)标记名称应小写。 为清晰起见,避免使用仅在大小写上有区别的名称,如 foo 和 Foo。同样,避免 使用 foobar 和 foo_bar 这样的名称。避免使用看上去相似的名称。在许多终端 和打印机上,“l”、“1”和“I”看上去非常相似。使用名为“l”的变量非常不明智,因为它 看上去非常象常量“1”。变量名变量名 选择变量名时,长度不重要,但清晰的表达很重要。长名称可用于全局变量,因为 它不常用,而将在每行循环上要使用的数组下标命名为 i 就完全够了。如果使用 “index”或“e

5、lementnumber”的话,不仅输入得更多,而且会使计算的细节不明确。 如果使用长变量名,有时候会使代码更难理解。比较:for(i=0 to 100)arrayi=0和for(elementnumber=0 to 100)arrayelementnumber=0;函数名函数名 函数名应反映函数执行什么操作以及返回什么内容。函数在表达式中使用,通常用 于 if 子句,因此它们的意图应一目了然。例如:if (checksize(x)没有帮助作用,因为它没有告诉我们 checksize 是在出错时返回 true 还是在不出 错时返回 true;而if (validsize(x)则使函数的意图很明

6、确。声明声明 所有的外部数据声明前都应加上 extern 关键字。 “指针”限定符“*”应紧邻变量名而不是类型。例如,应使用char *s, *t, *u;而不是char* s, t, u;后一条语句没有错,但可能不是我们期望的,因为没有将“t”和“u”声明为指针。头文件头文件 头文件应按功能组织在一起,即,对单独子系统的声明应在单独的头文件中。此外, 当代码从一个平台移植到另一个平台时有可能发生更改的声明应位于单独的头文件 中。避免使用与库头文件名相同的专用头文件名。语句 #include “math.h“ 如果在当前 目录中找不到所期望文件的话,会包括标准库 math 头文件。如果这是您期

7、望的 结果,可以注释掉这行 include 语句。 最后说明一点,对头文件使用绝对路径名不是一个好主意。C 编译器的“include- path”选项(在许多系统上为 -I 大写的 i)是处理众多专用头文件库的首选方法; 它允许在不改变源文件的情况下重新组织目录结构。scanf 在重要的应用程序中永远不要使用 scanf。它的错误检测不够完善。请看下面的示 例:#include int main(void)int i;float f;printf(“Enter an integer and a float: “);scanf(“%d %f“, printf(“I read %d and %fn

8、“, i, f);return 0;测试运行测试运行 Enter an integer and a float: 182 52.38 I read 182 and 52.380001 另一个测试运行另一个测试运行 Enter an integer and a float: 6713247896 4.4 I read -1876686696 and 4.400000+ 和和 - 当对语句中的变量使用递增或递减运算符时,该变量不应在语句中出现一次以上, 因为求值的顺序取决于编译器。编写代码时不要对顺序作假设,也不要编写在某一 机器上能够如期运作但没有明确定义的行为的代码:int i = 0, a5

9、;ai = i+;/* assign to a0? or a1? */不要被表面现象迷惑不要被表面现象迷惑 请看以下示例:while (c = t | c = | c = n)c = getc(f);乍一看, while 子句中的语句似乎是有效的 C 代码。但是,使用赋值运算符而不 是比较运算符却产生了语义上不正确的代码。= 的优先级在所有运算符中是最低的, 因此将以下列方式解释该语句(为清晰起见添加了括号):while (c = t | c) = ( | c = n)c = getc(f);赋值运算符左边的子句是:(c = t | c)它不会产生左值。如果 c 包含制表符,则结果是“true

10、”,并且不会执行进一步的求 值,而“true”不能位于赋值表达式的左边。意图要明确。意图要明确。 当您编写的代码可以解释成另一种意图时,使用括号或用其它方法以确保您的意图 清楚。如果您以后必须处理该程序的话,这有助于您理解您当初的意图。如果其他 人要维护该代码,这可以让维护任务变得更简单。 用能预见可能出现错误的方式编码,有时是可行的。例如,可以将常量放在比较等 式的左边。即,不编写:while (c = t | c = | c = n)c = getc(f);而是编写:while (t = c | = c | n = c)c = getc(f);用以下方法却会得到编译器诊断:while (t

11、 = c | = c | n = c)c = getc(f);这种风格让编译器发现问题;上面的语句是无效的,因为它试图对“t”赋值。意想不到的麻烦。意想不到的麻烦。 各种 C 实现通常在某些方面各有不同。坚持使用语言中可能对所有实现都是公共 的部分会有帮助。通过这样做,您更容易将程序移植到新的机器或编译器,并且不 大会遇到编译器特殊性所带来的问题。例如,考虑字符串:/*/*/2*/*/1这里利用了“最大适合(maximal munch)”规则。如果可以嵌套注释,则可将该字 符串解释为:/* /* /2 */ * */ 1两个 /* 符号与两个 */ 符号匹配,因此该字符串的值为 1。如果注释不

12、嵌套,那 么在有些系统上,注释中的 /* 就被忽略。在另一些系统上会针对 /* 发出警告。 无论哪种情况,该表达式可解释为:/* / */ 2 * /* */ 12 * 1 求值得 2。清空输出缓冲区清空输出缓冲区 当应用程序异常终止时,其输出的尾部常常会丢失。应用程序可能没有机会完全清 空它的输出缓冲区。输出的某一部分可能仍在内存中,并且永远不会被写出。在有 些系统上,这一输出可能有几页长。 以这种方式丢失输出会使人误解,因为它给人的印象是程序在它实际失败很久之前 就失败了。解决这一问题的方法是强制将输出从缓冲区清除,特别是在调试期间。 确切的方法随系统的不同而有所不同,不过也有常用的方法,

13、如下所示:setbuf(stdout, (char *) 0);必须在将任何内容写到标准输出之前执行该语句。理想情况下,这将是主程序中的 第一条语句。getchar() 宏还是函数宏还是函数 以下程序将其输入复制到其输出:#include int main(void)register int a;while (a = getchar() != EOF)putchar(a);从该程序除去 #include 语句将使该程序无法编译,因为 EOF 将是未定义的。 我们可以用以下方法重新编写该程序:#define EOF -1int main(void)register int a;while (a

14、= getchar() != EOF)putchar(a);这在许多系统上都可行,但在有些系统上运行要慢很多。 因为函数调用通常要花较长时间,所以常常把 getchar 实现为宏。这个宏定义在stdio.h 中,所以当除去 #include 时,编译器就不知道 getchar 是什 么。在有些系统上,假设 getchar 是返回一个 int 的函数。实际上,许多 C 实现在其库中都有 getchar 函数,部分原因是为了防止这样的失 误。于是,在 #include 遗漏的情况下,编译器使用 getchar 的函数版 本。函数调用的开销使程序变慢。 putchar 有同样的问题。空指针空指针 空

15、指针不指向任何对象。因此,为了赋值和比较以外的目的而使用空指针都是非法 的。 不要重新定义 NULL 符号。NULL 符号应始终是常量值零。任何给定类型的空指 针总是等于常量零,而与值为零的变量或与某一非零常量的比较,其行为由实现定 义。 反引用 null 指针可能会导致奇怪的事情发生。a+b 表示什么?表示什么? 解析它的唯一有意义的方法是:a + + + b然而,“最大适合”规则要求将它分解为:a + + + b这在语法上是无效的:它等于:(a+)+) + b但 a+ 的结果不是 左值左值,因此作为 + 的操作数是不可接受的。于是,解析词 法不明确性的规则使得以语法上有意义的方式解析该示例变得不可能。当然,谨慎 的办法实际上是在不能完全确定它们的意义的情况下,避免这样的构造。当然,添 加空格有助于编译器理解语句的意图,但(从代码维护的角度看)将这一构造分割 成多行更可取:+b;(a+) + b;小心处理函数小心

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 高等教育 > 其它相关文档

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号