Linux有獨(dú)特的編程風(fēng)格,在內(nèi)核源代碼目錄Documentation/CodingStyle,詳細(xì)描述代碼風(fēng)格。
建議大家可以去看一下,老外寫技術(shù)文檔還是很有意思的,上來就狂噴,“你不這樣寫就會完蛋,異教徒才不這樣寫……”,沒有國內(nèi)那么刻板,多閱讀英語文檔對技術(shù)增長很有幫助。
1. 命名規(guī)范
在一般編程中,習(xí)慣以如下方式命名宏、變量和函數(shù):
#define PI 3.1415926 /*用大寫字母代表宏 */
int minValue, maxValue; /*變量:第一個單詞全小寫,其后單詞的第一個字母大寫*/
void SendData (void); /* 函數(shù):所有單詞第一個字母都大寫 */
這種通過單詞之間通過首字母大寫來區(qū)分的方式非常流行。通過第1個單詞的首字母是否大寫可以區(qū)分名稱屬于變量還是屬于函數(shù),而看到整串的大寫字母可以斷定為宏。
許多領(lǐng)域的程序開發(fā)都遵照此習(xí)慣。
但是Linux不以這種習(xí)慣命名,對于上面的一段程序,在Linux中它會被命名為:
#define PI 3.1415926
int min_value, max_value;
void send_data (void);
在上述命名方式中,宏還是一樣用大寫,但變量和函數(shù)名,不按照Windows所采用的用首字母大寫來區(qū)分單詞,而是采用下劃線。而且Linux下命名,全局變量命名最好用長的準(zhǔn)確的描述,局部變量最好簡短,甚至直接用tmp,i之類的。
其實兩種命名方式都行,寫Liunx下的程序時,與Linux社區(qū)代碼風(fēng)格一致更好,但你用第一種我覺得也無傷大雅。
2.縮進(jìn)
縮進(jìn)統(tǒng)一使用"TAB",而不是空格括號。
另外提一句:在Linux下,"TAB"代表8個字符,而不是4個,Linux代碼風(fēng)格認(rèn)為8個字符更能體現(xiàn)層次結(jié)構(gòu)。文檔里噴"TAB"為4字符的是異教徒,對于8字符在多層次時,代碼太偏右的問題,文檔又噴層次超過三層,你的代碼就會完蛋,哈哈哈。
為了減少層次,在switch/case語句方面, Linux 建議switch和case對齊,例如:
switch (suffix) {
case 'G':
case 'g':
mem < <= 30;
break;
case 'M':
case 'm':
mem < <= 20;
break;
case 'K':
case 'k':
mem < <= 10;
/* fall through */
default:
break;
}
3. Linux中代碼括號“{”和“}”的使用原則
1)對于結(jié)構(gòu)體、if/for/while/switch語句, “{”不另起一行,例如:
struct var_data {
int len;
char data[0];
};
if (a == b) {
a = c;
d = a;
}
for (i = 0; i < 10; i++) {
a = c;
d = a;
}
2)如果if、for循環(huán)后只有1行,不要加“{”和“}”,例如:
for (i = 0; i < 10; i++) {
a = c;
}
應(yīng)該改為:
for (i = 0; i < 10; i++)
a=c;
- if和else混用的情況下, else語句不另起一行,例如:
if (x == y){
...
} else if (x > y) {
...
} else {
...
}
4)對于函數(shù), “{”另起一行,例如:
int add (int a, int b)
[
return a + b;
}
4. 空格的使用
1)關(guān)鍵字后加空格
在這些關(guān)鍵字后面加空格:
if, switch, case, for, do, while
但是這些不加:
sizeof, typeof, alignof, attribute
例如:
s = sizeof(struct file);
2)括號內(nèi),緊挨著括號不加空格
錯誤示范:
s = sizeof( struct file );
3)對于指針,”*“號挨著名字而不是類型
例如:
char *linux_banner;
unsigned long long memparse(char *ptr, char **retptr);
char *match_strdup(substring_t *s);
4)操作符兩側(cè)加空格
以下二元或三元操作符兩側(cè)要加空格:
= + - < > * / % | & ^ <= >= == != ? :
但是以下一元操作符,不加空格:
& * + - ~ ! sizeof typeof alignof attribute defined
自增自減符號。不加空格:
++ --
結(jié)構(gòu)體成員操作符,不加空格:
'.' 和 "->"
不要在行尾加空格。
5. 函數(shù)
函數(shù)應(yīng)該簡潔明了,只做一件事。函數(shù)長度應(yīng)該控制在一到兩個屏幕顯示范圍內(nèi)(ISO/ANSI屏幕大小是80x24)。
如果你有一個概念上簡單的函數(shù),那么使用一個更長的函數(shù)是可以的,比如函數(shù)里是case。
在源文件中,用空行分隔函數(shù)。如果函數(shù)被導(dǎo)出,它的EXPORT*宏應(yīng)該緊跟在結(jié)束的函數(shù)大括號行之后。例如:
int system_is_up(void)
{
return system_state == SYSTEM_RUNNING;
}
EXPORT_SYMBOL(system_is_up);
6. goto語句的使用
用不用goto一直是一個著名的爭議話題, Linux內(nèi)核源代碼中對goto的應(yīng)用非常廣泛,但是一般只限于錯誤處理中,其結(jié)構(gòu)如:
if(register_a() != 0)
goto err;
if (register_b() != 0)
goto errl;
if(register_c() != 0)
goto err2;
if (register_d () != 0)
goto err3;
err3:
unregister_c();
err2:
unregister_b ();
errl:
unregister_a ();
err:
return ret;
這種將goto用于錯誤處理的用法實在是簡單而高效,只需保證在錯誤處理時注銷、資源釋放等,與正常的注冊、資源申請順序相反。
7. 注釋
Linux風(fēng)格的注釋是c89 "/… /”風(fēng)格。不要使用c99風(fēng)格的“//…”注釋。
長(多行)注釋的首選樣式是:
/*
* This is the preferred style for multi-line
* comments in the Linux kernel source code.
* Please use it consistently.
*
* Description: A column of asterisks on the left side,
* with beginning and ending almost-blank lines.
*/
8.do {} while(0) 語句
在Linux內(nèi)核中,經(jīng)常會看到do {} while(0)這樣的語句。
許多人開始都會疑惑,認(rèn)為do while(0)毫無意義,因為它只會執(zhí)行一次,加不加do {} while(0)效果是完全一樣的,其實do {} while(0)的用法主要用于宏定義中。
這里用一個簡單的宏來演示:
#define SAFE_FREE (p) do{ free (p); p = NULL;} while (0)
假設(shè)這里去掉do...while(0),即定義SAFE_DELETE為:
#define SAFE FREE (p) free (p); p = NULL;
那么以下代碼:
if (NULL != p)
SAFE_DELETE(p)
else
.../* do something */
會被展開為:
if (NULL != P)
free (p); p = NULL;
else
.../* do something */
展開的代碼中存在兩個問題:
1)因為if分支后有兩個語句,導(dǎo)致else分支沒有對應(yīng)的if,編譯失敗。
2)假設(shè)沒有else分支,則SAFE_FREE中的第二個語句無論if測試是否通過,都會執(zhí)行。
的確,將SAFE_FREE的定義加上{}就可以解決上述問題了,即:
#define SAFE_FREE (p) { free (p); p= NULL; }
這樣,代碼:
if (NULL != p)
SAFE_DELETE(p)
else
... /* do something */
會被展開為:
if (NULL != p)
{ free (p); P = NULL; }
else
.../* do something */
但是,在C程序中,在每個語句后面加分號是一種約定俗成的習(xí)慣,那么,如下代碼:
if (NULL != p)
SAFE_DELETE (p);
else
.... /* do something */
將被擴(kuò)展為:
if (NULL != p)
{ free (p); p = NULL; };
else
.../* do something */
這樣else分支就又沒有對應(yīng)的if了,編譯將無法通過。
假設(shè)用了do {} while(0)語句,情況就不一樣了,同樣的代碼會被展開為:
if (NULL != p)
do{ free (p); p= NULL; } while (0);
else
.../* do something */
而不會再出現(xiàn)編譯問題。do{} while(0)的使用完全是為了保證宏定義的使用者能無編譯錯誤地使用宏,它不對其使用者做任何假設(shè)。
9. 其他規(guī)定
1.原則上不使用typedef,Linux認(rèn)為typedef會使可讀寫變差
2.內(nèi)聯(lián)函數(shù)通常不超過三行
評論