我們知道C語言的語法對縮進和空白沒有要求,空格、Tab、換行都可以隨意寫,實現同樣功能的代碼可以寫得很好看,也可以寫得很難看。例如上一章例 8.5 “剪刀石頭布”的代碼如果寫成這樣就很難看了:
例 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、由於UNIX系統標準的字元終端是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。在標準的字元終端上一個Tab看起來是8個空格的寬度,如果你的文本編輯器可以設置Tab的顯示寬度是幾個空格,建議也設成8,這樣大的縮進使代碼看起來非常清晰。如果有的行用空格做縮進,有的行用Tab做縮進,甚至空格和Tab混用,那麼一旦改變了文本編輯器的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; }