![C语言最佳实践](https://wfqqreader-1252317822.image.myqcloud.com/cover/885/53286885/b_53286885.jpg)
1.2.1 坏代码实例
具有上述这些问题的坏代码可以说是随处可见。程序清单1.1是一段C代码,它实现了一个简单的链表。
程序清单1.1 一个简单的链表实现(不良编码风格)
typedef struct Linklist{ ⇽--- 1.应避免使用typedef类型定义。2.Linklist的命名不当。3.字符{前应该有空格。 const char * elem; struct Linklist * next; }linklist; ⇽--- 4.字符}后应该有空格。5.linklist的命名不当。 //初始化链表的函数 ⇽--- 6.在C程序中,应避免使用C++风格的注释。7.应避免使用中文做注释。8.//后缺少必要的空格。 linklist * initlinklist(); ⇽--- 9.initlinklist的命名不当。10.函数声明不规范。11.表示指针的字符*应该紧贴后面的变量名或函数名。 const char * titles[]={"第1章 提高代码可读性","第2章 用好写好头文件", "第3章 消除编译警告","第4章 常量的定义和使用", "第5章 充分利用构建系统生成器"}; ⇽--- 12.书写拥挤,=两边应有空格。13.未适当换行和缩进。 int main() { ⇽--- 14.main函数的原型定义不规范。15.用于定义函数体的起始字符{应另起一行。 // 使用章节标题初始化链表 printf("初始化链表为:\n"); linklist *p=initlinklist(); ⇽--- 16.赋值运算符=的两边应有空格。 display(p); ⇽--- 17.未事先声明display()函数。18.display这一术语的选择不恰当,应考虑使用dump。 return 0; } linklist * initlinklist(){ ⇽--- 19.函数体之间应有空行,以便于阅读。20.函数原型的定义不规范。21.表示函数返回值类型为指针的字符*应该紧贴后面的函数名。22.字符{的前面应该有空格。 linklist * p=NULL; //创建头指针 ⇽--- 23.表示变量类型为指针的*应该紧贴变量名。24.赋值运算符=的两边应有空格。 linklist * temp = ( linklist*)malloc(sizeof(linklist)); ⇽--- 25.星号*和temp之间不应该有空格。26.左括号(和类型名称linklist之间有多余的空格。 // 先初始化首元节点 ⇽--- 27.使用了不规范的术语“首元节点”。 temp->elem = titles[0]; temp->next = NULL; p = temp; // 头指针指向首元节点 for (int i=1; i<5; i++) { ⇽--- 28.等号=和小于号<的两边应该有空格。 linklist *a=(linklist*)malloc(sizeof(linklist)); a->elem=titles[i]; a->next=NULL; temp->next=a; temp=temp->next; ⇽--- 29.以上5行中等号=的两边都应该有空格。 } 30.未缩进,应和定义循环体的for语句对齐。 return p; ⇽--- 31.return语句之前应有空行,用于分隔不同的功能块。32.应缩进。 }
可以看到,在区区几十行的代码中,我们罗列了30多个问题(当然,大多数问题是重复的)。我们可以将这些问题归纳如下。
● 排版问题:排版问题主要表现在对齐、缩进、空格和空行的不恰当使用上。比如上述代码在该使用空格的地方没有使用空格,有些不该使用空格的地方却使用空格。这一方面使代码不够清晰和整洁,另一方面也表现出代码的作者在编码时非常随意。
● 命名问题:这段代码中存在明显的命名不当问题。代码中变量和函数的命名既要注意语法,也要注意命名风格。在一开始的typedef
类型定义中,Linklist
这个名称同时存在这两个问题。链表的标准英语名称是linked list,在把这两个单词组合在一起构成类型名时,应该采用LinkedList
或linked_list
这样的形式。类似地,变量名称linklist
在风格和语法上都存在问题。再如initlinklist()
这个函数,其命名也不规范,几个小写单词挤在一起既拥挤又难看。如果使用init_linked_list()
作为函数名,用下画线将各个单词分隔,则会明显提高代码的可读性。
● 语法问题:这段代码存在两方面语法问题,一方面是不必要的typedef
类型定义,另一方面是错误的main
函数等的原型声明。比如这段代码中定义的链表结构体,并不需要使用typedef
定义为一种新的数据类型,在代码中直接使用struct linked_list
作为类型名称显然要比使用新定义的类型名称linklist
更加清晰。原因在于通常我们会将类型定义放到头文件中,当我们在某个源代码文件中阅读到使用struct linked_list *
的代码时,不需要查看头文件便可知悉该代码定义了一个结构体指针,而非整数、枚举量或者结构体。在本章的后面,我们将给出合理使用typedef
的几个建议。另外,在这段代码中,int main()
的写法并不规范,规范的写法要么是int main(void)
,要么是int main(int argc, const char *args[])
。
● 注释问题:这段代码中的原始注释采用了C++风格的注释(//
打头),另外注释内容使用了中文。尽管强制非英语母语的程序员使用英文写注释有些刻板或者严苛,但如果我们考虑到开源大势以及可能的国际交流,尽量用英语书写注释无疑是一种合理的要求。笔者不鼓励在产品级代码中使用中文注释的另外一个原因是,大量的计算机软硬件术语最初源自英文,当我们使用中文时,由于理解或者表述上的问题,就会出现各种偏差。比如在上述代码中,“首元节点”这一术语就显得非常怪异。我们可以轻松理解“首”,这里大概就是指第一个;但“首元”或者“首元节点”是何意?另外,在C代码中,仅在简短的行尾注释中使用C++引入的注释方法,也是应该遵循的一个原则——这看起来有点古板,却是优秀和专业的C程序员始终需要遵循的一项传统或者习惯。