我们知道C语言的语法对编码风格并没有要求,空格、Tab和换行都可以自己随意写,实现同样功能的代码可以写得很好看,也可以写得很难看。例如例 8.3 “剪刀石头布”那个程序如果写成这样就很难看了:
例 9.1. 缺少缩进和空白的代码
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void) { char gesture[3][10]={"scissor","stone","cloth"}; int man,computer,result, ret; srand(time(NULL)); while(1){ computer=rand()%3; printf("\nInput your gesture (0-scissor 1-stone 2-cloth):\n"); ret=scanf("%d",&man); if(ret!=1||man<0||man>2){ printf("Invalid input! Please input 0, 1 or 2.\n"); continue; } printf("Your gesture: %s\tComputer's gesture: %s\n",gesture[man],gesture[computer]); result=(man-computer+4)%3-1; if(result>0)printf("You win!\n"); else if(result==0)printf("Draw!\n"); else printf("You lose!\n"); } return 0; }
一是没有空白符(包括必要的换行),代码密度太大,看着很费劲。二是没有缩进,看不出来哪个{和哪个}配对,像这么短的代码还能凑合着看,如果代码超过一屏就完全不可读了。[CodingStyle]中关于空白符并没有特别规定,因为基本上所有的C代码风格对于空白符的规定都差不多,主要有以下几条。
1、关键字if, while, for与其后的控制表达式的(括号之间插入一个空格分隔,但括号内的表达式应紧贴括号。例如:
while␣(1);
2、双目运算符的两侧插入一个空格分隔,单目运算符和操作数之间不加空格,例如i␣=␣i␣+␣1
、++i
、!(i␣<␣1)
、-x
、&a[1]
等。
3、后缀运算符和操作数之间也不加空格,例如取结构体成员s.a
、函数调用foo(arg1)
、取数组成员a[i]
。
4、,号和;号之后要加空格,这是英文的书写习惯,例如for␣(i␣=␣1;␣i␣<␣10;␣i++)
、foo(arg1,␣arg2)
。
5、以上关于双目运算符和后缀运算符的规则不是严格要求,有时候为了突出优先级也可以写得更紧凑一些,例如for␣(i=1;␣i<10;␣i++)
、distance␣=␣sqrt(x*x␣+␣y*y)
等。但是省略的空格一定不要误导了读代码的人,例如a||b␣&&␣c
很容易让人理解成错误的优先级。
6、由于标准的Linux终端是24行80列的,接近或大于80个字符的较长语句要折行写,折行后用空格和上面的表达式或参数对齐,例如:
if␣(sqrt(x*x␣+␣y*y) > 5.0 &&␣x␣<␣0.0 &&␣y␣>␣0.0)
再比如:
foo(sqrt(x*x␣+␣y*y), a[i-1]␣+␣b[i-1]␣+␣c[i-1])
7、较长的字符串可以断成多个字符串然后分行书写,例如:
printf("This is such a long sentence that " "it cannot be held within a line\n");
C编译器会自动把相邻的多个字符串接在一起,以上两个字符串相当于一个字符串"This is such a long sentence that it cannot be held within a line\n"
。
8、有的人喜欢在变量定义语句中用Tab字符,使变量名对齐,这样看起来也很好,但不是严格要求的。
→int →a, b; →double →c;
内核关于缩进的规则有以下几条。
1、要用缩进体现出语句块的层次关系,使用Tab字符缩进,不能用空格代替Tab。在标准的Linux终端上,一个Tab看起来是8个空格的宽度,有些编辑器可以设置一个Tab看起来是几个空格的宽度,建议设成8,这样大的缩进使代码看起来非常清晰。规定不能用空格代替Tab主要是不希望空格和Tab混在一起做缩进,如果混在一起用了,在某些编辑器里把Tab的宽度改了就会看起来非常混乱。
2、if/else
、while
、do/while
、for
、switch
这些可以带语句块的语句,语句块的{和}应该和关键字写在一起,用空格隔开,而不是单独占一行。例如应该这样写:
if␣(...)␣{ →语句列表 }␣else␣if␣(...)␣{ →语句列表 }
当然,其实更多人习惯这样写:
if␣(...) { →语句列表 } else␣if␣(...) { →语句列表 }
内核的写法和[K&R]一致,好处是不必占用太多空行,使得一屏能显示更多代码。这两种写法用得都很广泛,只要在同一个项目中能保持统一就可以了。
3、函数定义的{和}单独占一行,这一点和语句块的规定不同,例如:
int␣foo(int␣a,␣int␣b) { →语句列表 }
4、switch
和语句块里的case
、default
对齐写,也就是说语句块里的case
、default
相对于switch
不往里缩进。例如:
→switch␣(c)␣{ →case 'A': → →语句列表 →case 'B': → →语句列表 →default: → →语句列表 →}
自己命名的标号(用于goto
)必须顶头写不缩进,而不管标号下的语句缩进到第几层。
5、代码中每个逻辑段落之间应该用一个空行分隔开。例如每个函数定义之间应该插入一个空行,头文件、全局变量定义和函数定义之间也应该插入空行,例如:
#include <stdio.h> #include <stdlib.h> int g; double h; int foo(void) { →语句列表 } int bar(int a) { →语句列表 } int main(void) { →语句列表 }
6、一个函数的语句列表如果很长,也可以根据相关性分成若干组,用空行分隔,这条规定不是严格要求,一般变量定义语句组成一组,后面要加空行,return
之前要加空行,例如:
int main(void) { →int →a, b; →double →c; →语句组1 →语句组2 →return 0; }