1. 程式人生 > >unix K&R編碼風格

unix K&R編碼風格

Chinese translated version of Documentation/CodingStyle


If you have any comment or update to the content, please post to LKML directly.
However, if you have problem communicating in English you can also ask the
Chinese maintainer for help.  Contact the Chinese maintainer, if this
translation is outdated or there is problem with translation.


Chinese maintainer: Zhang Le <
[email protected]
>
---------------------------------------------------------------------
Documentation/CodingStyle的中文翻譯


如果想評論或更新本文的內容,請直接發信到LKML。如果你使用英文交流有困難的話,也可
以向中文版維護者求助。如果本翻譯更新不及時或者翻譯存在問題,請聯絡中文版維護者。


中文版維護者: 張樂 Zhang Le <[email protected]>
中文版翻譯者: 張樂 Zhang Le <[email protected]
>
中文版校譯者: 王聰 Wang Cong <[email protected]>
               wheelz <[email protected]>
               管旭東 Xudong Guan <[email protected]l.com>
               Li Zefan <[email protected]>
               Wang Chen <[email protected]>
以下為正文
---------------------------------------------------------------------


Linux核心程式碼風格


這是一個簡短的文件,描述了linux核心的首選程式碼風格。程式碼風格是因人而異的,而且我
不願意把我的觀點強加給任何人,不過這裡所講述的是我必須要維護的程式碼所遵守的風格,
並且我也希望絕大多數其他程式碼也能遵守這個風格。請在寫程式碼時至少考慮一下本文所述的
風格。


首先,我建議你列印一份GNU程式碼規範,然後不要讀它。燒了它,這是一個具有重大象徵性
意義的動作。


不管怎樣,現在我們開始:




第一章:縮排


製表符是8個字元,所以縮排也是8個字元。有些異端運動試圖將縮排變為4(乃至2)個字元
深,這幾乎相當於嘗試將圓周率的值定義為3。


理由:縮排的全部意義就在於清楚的定義一個控制塊起止於何處。尤其是當你盯著你的螢幕
連續看了20小時之後,你將會發現大一點的縮排會使你更容易分辨縮排。


現在,有些人會抱怨8個字元的縮排會使程式碼向右邊移動的太遠,在80個字元的終端螢幕上
就很難讀這樣的程式碼。這個問題的答案是,如果你需要3級以上的縮排,不管用何種方式你
的程式碼已經有問題了,應該修正你的程式。


簡而言之,8個字元的縮排可以讓程式碼更容易閱讀,還有一個好處是當你的函式巢狀太深的
時候可以給你警告。留心這個警告。


在switch語句中消除多級縮排的首選的方式是讓“switch”和從屬於它的“case”標籤對齊於同
一列,而不要“兩次縮排”“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;
}




不要把多個語句放在一行裡,除非你有什麼東西要隱藏:


if (condition) do_this;
 do_something_everytime;


也不要在一行裡放多個賦值語句。核心程式碼風格超級簡單。就是避免可能導致別人誤讀的表
達式。


除了註釋、文件和Kconfig之外,不要使用空格來縮排,前面的例子是例外,是有意為之。


選用一個好的編輯器,不要在行尾留空格。




第二章:把長的行和字串打散


程式碼風格的意義就在於使用平常使用的工具來維持程式碼的可讀性和可維護性。


每一行的長度的限制是80列,我們強烈建議您遵守這個慣例。


長於80列的語句要打散成有意義的片段。每個片段要明顯短於原來的語句,而且放置的位置
也明顯的靠右。同樣的規則也適用於有很長引數列表的函式頭。長字串也要打散成較短的
字串。唯一的例外是超過80列可以大幅度提高可讀性並且不會隱藏資訊的情況。


void fun(int a, int b, int c)
{
if (condition)
printk(KERN_WARNING "Warning this is a long printk with "
"3 parameters a: %u b: %u "
"c: %u \n", a, b, c);
else
next_statement;
}


第三章:大括號和空格的放置


C語言風格中另外一個常見問題是大括號的放置。和縮排大小不同,選擇或棄用某種放置策
略並沒有多少技術上的原因,不過首選的方式,就像Kernighan和Ritchie展示給我們的,是
把起始大括號放在行尾,而把結束大括號放在行首,所以:


if (x is true) {
we do y
}


這適用於所有的非函式語句塊(if、switch、for、while、do)。比如:


switch (action) {
case KOBJ_ADD:
return "add";
case KOBJ_REMOVE:
return "remove";
case KOBJ_CHANGE:
return "change";
default:
return NULL;
}


不過,有一個例外,那就是函式:函式的起始大括號放置於下一行的開頭,所以:


int function(int x)
{
body of function
}


全世界的異端可能會抱怨這個不一致性是……呃……不一致的,不過所有思維健全的人都知道(
a)K&R是_正確的_,並且(b)K&R是正確的。此外,不管怎樣函式都是特殊的(在C語言中
,函式是不能巢狀的)。


注意結束大括號獨自佔據一行,除非它後面跟著同一個語句的剩餘部分,也就是do語句中的
“while”或者if語句中的“else”,像這樣:


do {
body of do-loop
} while (condition);





if (x == y) {
..
} else if (x > y) {
...
} else {
....
}


理由:K&R。


也請注意這種大括號的放置方式也能使空(或者差不多空的)行的數量最小化,同時不失可
讀性。因此,由於你的螢幕上的新行是不可再生資源(想想25行的終端螢幕),你將會有更
多的空行來放置註釋。


當只有一個單獨的語句的時候,不用加不必要的大括號。


if (condition)
action();


這點不適用於本身為某個條件語句的一個分支的單獨語句。這時需要在兩個分支裡都使用大
括號。


if (condition) {
do_this();
do_that();
} else {
otherwise();
}


3.1:空格


Linux核心的空格使用方式(主要)取決於它是用於函式還是關鍵字。(大多數)關鍵字後
要加一個空格。值得注意的例外是sizeof、typeof、alignof和__attribute__,這些關鍵字
某些程度上看起來更像函式(它們在Linux裡也常常伴隨小括號而使用,儘管在C語言裡這樣
的小括號不是必需的,就像“struct fileinfo info”宣告過後的“sizeof info”)。


所以在這些關鍵字之後放一個空格:
if, switch, case, for, do, while
但是不要在sizeof、typeof、alignof或者__attribute__這些關鍵字之後放空格。例如,
s = sizeof(struct file);


不要在小括號裡的表示式兩側加空格。這是一個反例:


s = sizeof( struct file );


當宣告指標型別或者返回指標型別的函式時,“*”的首選使用方式是使之靠近變數名或者函
數名,而不是靠近型別名。例子:


char *linux_banner;
unsigned long long memparse(char *ptr, char **retptr);
char *match_strdup(substring_t *s);


在大多數二元和三元操作符兩側使用一個空格,例如下面所有這些操作符:


=  +  -  <  >  *  /  %  |  &  ^  <=  >=  ==  !=  ?  :


但是一元操作符後不要加空格:
&  *  +  -  ~  !  sizeof  typeof  alignof  __attribute__  defined


字尾自加和自減一元操作符前不加空格:
++  --


字首自加和自減一元操作符後不加空格:
++  --


“.”和“->”結構體成員操作符前後不加空格。


不要在行尾留空白。有些可以自動縮排的編輯器會在新行的行首加入適量的空白,然後你
就可以直接在那一行輸入程式碼。不過假如你最後沒有在那一行輸入程式碼,有些編輯器就不
會移除已經加入的空白,就像你故意留下一個只有空白的行。包含行尾空白的行就這樣產
生了。


當git發現補丁包含了行尾空白的時候會警告你,並且可以應你的要求去掉行尾空白;不過
如果你是正在打一系列補丁,這樣做會導致後面的補丁失敗,因為你改變了補丁的上下文。




第四章:命名


C是一個簡樸的語言,你的命名也應該這樣。和Modula-2和Pascal程式設計師不同,C程式設計師不使
用類似ThisVariableIsATemporaryCounter這樣華麗的名字。C程式設計師會稱那個變數為“tmp”
,這樣寫起來會更容易,而且至少不會令其難於理解。


不過,雖然混用大小寫的名字是不提倡使用的,但是全域性變數還是需要一個具描述性的名字
。稱一個全域性函式為“foo”是一個難以饒恕的錯誤。


全域性變數(只有當你真正需要它們的時候再用它)需要有一個具描述性的名字,就像全域性函
數。如果你有一個可以計算活動使用者數量的函式,你應該叫它“count_active_users()”或者
類似的名字,你不應該叫它“cntuser()”。


在函式名中包含函式型別(所謂的匈牙利命名法)是腦子出了問題——編譯器知道那些型別而
且能夠檢查那些型別,這樣做只能把程式設計師弄糊塗了。難怪微軟總是製造出有問題的程式。


本地變數名應該簡短,而且能夠表達相關的含義。如果你有一些隨機的整數型的迴圈計數器
,它應該被稱為“i”。叫它“loop_counter”並無益處,如果它沒有被誤解的可能的話。類似
的,“tmp”可以用來稱呼任意型別的臨時變數。


如果你怕混淆了你的本地變數名,你就遇到另一個問題了,叫做函式增長荷爾蒙失衡綜合症
。請看第六章(函式)。




第五章:Typedef


不要使用類似“vps_t”之類的東西。


對結構體和指標使用typedef是一個錯誤。當你在程式碼裡看到:


vps_t a;


這代表什麼意思呢?


相反,如果是這樣


struct virtual_container *a;


你就知道“a”是什麼了。


很多人認為typedef“能提高可讀性”。實際不是這樣的。它們只在下列情況下有用:


 (a) 完全不透明的物件(這種情況下要主動使用typedef來隱藏這個物件實際上是什麼)。


     例如:“pte_t”等不透明物件,你只能用合適的訪問函式來訪問它們。


     注意!不透明性和“訪問函式”本身是不好的。我們使用pte_t等型別的原因在於真的是
     完全沒有任何共用的可訪問資訊。


 (b) 清楚的整數型別,如此,這層抽象就可以幫助消除到底是“int”還是“long”的混淆。


     u8/u16/u32是完全沒有問題的typedef,不過它們更符合類別(d)而不是這裡。


     再次注意!要這樣做,必須事出有因。如果某個變數是“unsigned long“,那麼沒有必要


typedef unsigned long myflags_t;


     不過如果有一個明確的原因,比如它在某種情況下可能會是一個“unsigned int”而在
     其他情況下可能為“unsigned long”,那麼就不要猶豫,請務必使用typedef。


 (c) 當你使用sparse按字面的建立一個新型別來做型別檢查的時候。


 (d) 和標準C99型別相同的型別,在某些例外的情況下。


     雖然讓眼睛和腦筋來適應新的標準型別比如“uint32_t”不需要花很多時間,可是有些
     人仍然拒絕使用它們。


     因此,Linux特有的等同於標準型別的“u8/u16/u32/u64”型別和它們的有符號型別是被
     允許的——儘管在你自己的新程式碼中,它們不是強制要求要使用的。


     當編輯已經使用了某個型別集的已有程式碼時,你應該遵循那些程式碼中已經做出的選擇。


 (e) 可以在使用者空間安全使用的型別。


     在某些使用者空間可見的結構體裡,我們不能要求C99型別而且不能用上面提到的“u32”
     型別。因此,我們在與使用者空間共享的所有結構體中使用__u32和類似的型別。


可能還有其他的情況,不過基本的規則是永遠不要使用typedef,除非你可以明確的應用上
述某個規則中的一個。


總的來說,如果一個指標或者一個結構體裡的元素可以合理的被直接訪問到,那麼它們就不
應該是一個typedef。




第六章:函式


函式應該簡短而漂亮,並且只完成一件事情。函式應該可以一屏或者兩屏顯示完(我們都知
道ISO/ANSI螢幕大小是80x24),只做一件事情,而且把它做好。


一個函式的最大長度是和該函式的複雜度和縮排級數成反比的。所以,如果你有一個理論上
很簡單的只有一個很長(但是簡單)的case語句的函式,而且你需要在每個case裡做很多很
小的事情,這樣的函式儘管很長,但也是可以的。


不過,如果你有一個複雜的函式,而且你懷疑一個天分不是很高的高中一年級學生可能甚至
搞不清楚這個函式的目的,你應該嚴格的遵守前面提到的長度限制。使用輔助函式,併為之
取個具描述性的名字(如果你覺得它們的效能很重要的話,可以讓編譯器內聯它們,這樣的
效果往往會比你寫一個複雜函式的效果要好。)


函式的另外一個衡量標準是本地變數的數量。此數量不應超過5-10個,否則你的函式就有
問題了。重新考慮一下你的函式,把它分拆成更小的函式。人的大腦一般可以輕鬆的同時跟
蹤7個不同的事物,如果再增多的話,就會糊塗了。即便你聰穎過人,你也可能會記不清你2
個星期前做過的事情。


在原始檔裡,使用空行隔開不同的函式。如果該函式需要被匯出,它的EXPORT*巨集應該緊貼
在它的結束大括號之下。比如:


int system_is_up(void)
{
return system_state == SYSTEM_RUNNING;
}
EXPORT_SYMBOL(system_is_up);


在函式原型中,包含函式名和它們的資料型別。雖然C語言裡沒有這樣的要求,在Linux裡這
是提倡的做法,因為這樣可以很簡單的給讀者提供更多的有價值的資訊。




第七章:集中的函式退出途徑


雖然被某些人聲稱已經過時,但是goto語句的等價物還是經常被編譯器所使用,具體形式是
無條件跳轉指令。


當一個函式從多個位置退出並且需要做一些通用的清理工作的時候,goto的好處就顯現出來
了。


理由是:


- 無條件語句容易理解和跟蹤
- 巢狀程度減小
- 可以避免由於修改時忘記更新某個單獨的退出點而導致的錯誤
- 減輕了編譯器的工作,無需刪除冗餘程式碼;)


int fun(int a)
{
int result = 0;
char *buffer = kmalloc(SIZE);


if (buffer == NULL)
return -ENOMEM;


if (condition1) {
while (loop1) {
...
}
result = 1;
goto out;
}
...
out:
kfree(buffer);
return result;
}


第八章:註釋


註釋是好的,不過有過度註釋的危險。永遠不要在註釋裡解釋你的程式碼是如何運作的:更好
的做法是讓別人一看你的程式碼就可以明白,解釋寫的很差的程式碼是浪費時間。


一般的,你想要你的註釋告訴別人你的程式碼做了什麼,而不是怎麼做的。也請你不要把註釋
放在一個函式體內部:如果函式複雜到你需要獨立的註釋其中的一部分,你很可能需要回到
第六章看一看。你可以做一些小注釋來註明或警告某些很聰明(或者槽糕)的做法,但不要
加太多。你應該做的,是把註釋放在函式的頭部,告訴人們它做了什麼,也可以加上它做這
些事情的原因。


當註釋核心API函式時,請使用kernel-doc格式。請看
Documentation/kernel-doc-nano-HOWTO.txt和scripts/kernel-doc以獲得詳細資訊。


Linux的註釋風格是C89“/* ... */”風格。不要使用C99風格“// ...”註釋。


長(多行)的首選註釋風格是:


/*
* 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.
*/


註釋資料也是很重要的,不管是基本型別還是衍生型別。為了方便實現這一點,每一行應只
宣告一個數據(不要使用逗號來一次宣告多個數據)。這樣你就有空間來為每個資料寫一段
小注釋來解釋它們的用途了。




第九章:你已經把事情弄糟了


這沒什麼,我們都是這樣。可能你的使用了很長時間Unix的朋友已經告訴你“GNU emacs”能
自動幫你格式化C原始碼,而且你也注意到了,確實是這樣,不過它所使用的預設值和我們
想要的相去甚遠(實際上,甚至比隨機打的還要差——無數個猴子在GNU emacs裡打字永遠不
會創造出一個好程式)(譯註:請參考Infinite Monkey Theorem)


所以你要麼放棄GNU emacs,要麼改變它讓它使用更合理的設定。要採用後一個方案,你可
以把下面這段貼上到你的.emacs檔案裡。


(defun linux-c-mode ()
  "C mode with adjusted defaults for use with the Linux kernel."
  (interactive)
  (c-mode)
  (c-set-style "K&R")
  (setq tab-width 8)
  (setq indent-tabs-mode t)
  (setq c-basic-offset 8))


這樣就定義了M-x linux-c-mode命令。當你hack一個模組的時候,如果你把字串
-*- linux-c -*-放在頭兩行的某個位置,這個模式將會被自動呼叫。如果你希望在你修改
/usr/src/linux裡的檔案時魔術般自動開啟linux-c-mode的話,你也可能需要新增


(setq auto-mode-alist (cons '("/usr/src/linux.*/.*\\.[ch]$" . linux-c-mode)
auto-mode-alist))


到你的.emacs檔案裡。


不過就算你嘗試讓emacs正確的格式化程式碼失敗了,也並不意味著你失去了一切:還可以用“
indent”。


不過,GNU indent也有和GNU emacs一樣有問題的設定,所以你需要給它一些命令選項。不
過,這還不算太糟糕,因為就算是GNU indent的作者也認同K&R的權威性(GNU的人並不是壞
人,他們只是在這個問題上被嚴重的誤導了),所以你只要給indent指定選項“-kr -i8”
(代表“K&R,8個字元縮排”),或者使用“scripts/Lindent”,這樣就可以以最時髦的方式
縮排原始碼。


“indent”有很多選項,特別是重新格式化註釋的時候,你可能需要看一下它的手冊頁。不過
記住:“indent”不能修正壞的程式設計習慣。




第十章:Kconfig配置檔案


對於遍佈原始碼樹的所有Kconfig*配置檔案來說,它們縮排方式與C程式碼相比有所不同。緊挨
在“config”定義下面的行縮排一個製表符,幫助資訊則再多縮排2個空格。比如:


config AUDIT
bool "Auditing support"
depends on NET
help
 Enable auditing infrastructure that can be used with another
 kernel subsystem, such as SELinux (which requires this for
 logging of avc messages output).  Does not do system-call
 auditing without CONFIG_AUDITSYSCALL.


仍然被認為不夠穩定的功能應該被定義為依賴於“EXPERIMENTAL”:


config SLUB
depends on EXPERIMENTAL && !ARCH_USES_SLAB_PAGE_STRUCT
bool "SLUB (Unqueued Allocator)"
...


而那些危險的功能(比如某些檔案系統的寫支援)應該在它們的提示字串裡顯著的宣告這
一點:


config ADFS_FS_RW
bool "ADFS write support (DANGEROUS)"
depends on ADFS_FS
...


要檢視配置檔案的完整文件,請看Documentation/kbuild/kconfig-language.txt。




第十一章:資料結構


如果一個數據結構,在建立和銷燬它的單線執行環境之外可見,那麼它必須要有一個引用計
數器。核心裡沒有垃圾收集(並且核心之外的垃圾收集慢且效率低下),這意味著你絕對需
要記錄你對這種資料結構的使用情況。


引用計數意味著你能夠避免上鎖,並且允許多個使用者並行訪問這個資料結構——而不需要擔心
這個資料結構僅僅因為暫時不被使用就消失了,那些使用者可能不過是沉睡了一陣或者做了一
些其他事情而已。


注意上鎖不能取代引用計數。上鎖是為了保持資料結構的一致性,而引用計數是一個記憶體管
理技巧。通常二者都需要,不要把兩個搞混了。


很多資料結構實際上有2級引用計數,它們通常有不同“類”的使用者。子類計數器統計子類用
戶的數量,每當子類計數器減至零時,全域性計數器減一。


這種“多級引用計數”的例子可以在記憶體管理(“struct mm_struct”:mm_users和mm_count)
和檔案系統(“struct super_block”:s_count和s_active)中找到。


記住:如果另一個執行線索可以找到你的資料結構,但是這個資料結構沒有引用計數器,這
裡幾乎肯定是一個bug。




第十二章:巨集,列舉和RTL


用於定義常量的巨集的名字及列舉裡的標籤需要大寫。


#define CONSTANT 0x12345


在定義幾個相關的常量時,最好用列舉。


巨集的名字請用大寫字母,不過形如函式的巨集的名字可以用小寫字母。


一般的,如果能寫成行內函數就不要寫成像函式的巨集。


含有多個語句的巨集應該被包含在一個do-while程式碼塊裡:


#define macrofun(a, b, c) \
do {\
if (a == 5) \
do_this(b, c);\
} while (0)


使用巨集的時候應避免的事情:


1) 影響控制流程的巨集:


#define FOO(x)\
do {\
if (blah(x) < 0)\
return -EBUGGERED;\
} while(0)


非常不好。它看起來像一個函式,不過卻能導致“呼叫”它的函式退出;不要打亂讀者大腦裡
的語法分析器。


2) 依賴於一個固定名字的本地變數的巨集:


#define FOO(val) bar(index, val)


可能看起來像是個不錯的東西,不過它非常容易把讀程式碼的人搞糊塗,而且容易導致看起來
不相關的改動帶來錯誤。


3) 作為左值的帶引數的巨集: FOO(x) = y;如果有人把FOO變成一個行內函數的話,這種用
法就會出錯了。


4) 忘記了優先順序:使用表示式定義常量的巨集必須將表示式置於一對小括號之內。帶引數的
巨集也要注意此類問題。


#define CONSTANT 0x4000
#define CONSTEXP (CONSTANT | 3)


cpp手冊對巨集的講解很詳細。Gcc internals手冊也詳細講解了RTL(譯註:register
transfer language),核心裡的組合語言經常用到它。




第十三章:列印核心訊息


核心開發者應該是受過良好教育的。請一定注意核心資訊的拼寫,以給人以好的印象。不要
用不規範的單詞比如“dont”,而要用“do not”或者“don't”。保證這些資訊簡單、明瞭、無
歧義。


核心資訊不必以句號(譯註:英文句號,即點)結束。


在小括號裡列印數字(%d)沒有任何價值,應該避免這樣做。


<linux/device.h>裡有一些驅動模型診斷巨集,你應該使用它們,以確保資訊對應於正確的
裝置和驅動,並且被標記了正確的訊息級別。這些巨集有:dev_err(), dev_warn(),
dev_info()等等。對於那些不和某個特定裝置相關連的資訊,<linux/kernel.h>定義了
pr_debug()和pr_info()。


寫出好的除錯資訊可以是一個很大的挑戰;當你寫出來之後,這些資訊在遠端除錯的時候
就會成為極大的幫助。當DEBUG符號沒有被定義的時候,這些資訊不應該被編譯進核心裡
(也就是說,預設地,它們不應該被包含在內)。如果你使用dev_dbg()或者pr_debug(),
就能自動達到這個效果。很多子系統擁有Kconfig選項來啟用-DDEBUG。還有一個相關的慣例
是使用VERBOSE_DEBUG來新增dev_vdbg()訊息到那些已經由DEBUG啟用的訊息之上。




第十四章:分配記憶體


核心提供了下面的一般用途的記憶體分配函式:kmalloc(),kzalloc(),kcalloc()和
vmalloc()。請參考API文件以獲取有關它們的詳細資訊。


傳遞結構體大小的首選形式是這樣的:


p = kmalloc(sizeof(*p), ...);


另外一種傳遞方式中,sizeof的運算元是結構體的名字,這樣會降低可讀性,並且可能會引
入bug。有可能指標變數型別被改變時,而對應的傳遞給記憶體分配函式的sizeof的結果不變。


強制轉換一個void指標返回值是多餘的。C語言本身保證了從void指標到其他任何指標型別
的轉換是沒有問題的。




第十五章:內聯弊病


有一個常見的誤解是行內函數是gcc提供的可以讓程式碼執行更快的一個選項。雖然使用內聯
函式有時候是恰當的(比如作為一種替代巨集的方式,請看第十二章),不過很多情況下不是
這樣。inline關鍵字的過度使用會使核心變大,從而使整個系統執行速度變慢。因為大核心
會佔用更多的指令快取記憶體(譯註:一級快取通常是指令快取和資料快取分開的)而且會導
致pagecache的可用記憶體減少。想象一下,一次pagecache未命中就會導致一次磁碟定址,將
耗時5毫秒。5毫秒的時間內CPU能執行很多很多指令。


一個基本的原則是如果一個函式有3行以上,就不要把它變成行內函數。這個原則的一個例
外是,如果你知道某個引數是一個編譯時常量,而且因為這個常量你確定編譯器在編譯時能
優化掉你的函式的大部分程式碼,那仍然可以給它加上inline關鍵字。kmalloc()行內函數就
是一個很好的例子。


人們經常主張給static的而且只用了一次的函式加上inline,如此不會有任何損失,因為沒
有什麼好權衡的。雖然從技術上說這是正確的,但是實際上這種情況下即使不加inline gcc
也可以自動使其內聯。而且其他使用者可能會要求移除inline,由此而來的爭論會抵消inline
自身的潛在價值,得不償失。




第十六章:函式返回值及命名


函式可以返回很多種不同型別的值,最常見的一種是表明函式執行成功或者失敗的值。這樣
的一個值可以表示為一個錯誤程式碼整數(-Exxx=失敗,0=成功)或者一個“成功”布林值(
0=失敗,非0=成功)。


混合使用這兩種表達方式是難於發現的bug的來源。如果C語言本身嚴格區分整形和布林型變
量,那麼編譯器就能夠幫我們發現這些錯誤……不過C語言不區分。為了避免產生這種bug,請
遵循下面的慣例:


如果函式的名字是一個動作或者強制性的命令,那麼這個函式應該返回錯誤程式碼整
數。如果是一個判斷,那麼函式應該返回一個“成功”布林值。


比如,“add work”是一個命令,所以add_work()函式在成功時返回0,在失敗時返回-EBUSY。
類似的,因為“PCI device present”是一個判斷,所以pci_dev_present()函式在成功找到
一個匹配的裝置時應該返回1,如果找不到時應該返回0。


所有匯出(譯註:EXPORT)的函式都必須遵守這個慣例,所有的公共函式也都應該如此。私
有(static)函式不需要如此,但是我們也推薦這樣做。


返回值是實際計算結果而不是計算是否成功的標誌的函式不受此慣例的限制。一般的,他們
通過返回一些正常值範圍之外的結果來表示出錯。典型的例子是返回指標的函式,他們使用
NULL或者ERR_PTR機制來報告錯誤。




第十七章:不要重新發明核心巨集


標頭檔案include/linux/kernel.h包含了一些巨集,你應該使用它們,而不要自己寫一些它們的
變種。比如,如果你需要計算一個數組的長度,使用這個巨集


  #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))


類似的,如果你要計算某結構體成員的大小,使用


  #define FIELD_SIZEOF(t, f) (sizeof(((t*)0)->f))


還有可以做嚴格的型別檢查的min()和max()巨集,如果你需要可以使用它們。你可以自己看看
那個標頭檔案裡還定義了什麼你可以拿來用的東西,如果有定義的話,你就不應在你的程式碼裡
自己重新定義。




第十八章:編輯器模式行和其他需要羅嗦的事情


有一些編輯器可以解釋嵌入在原始檔裡的由一些特殊標記標明的配置資訊。比如,emacs
能夠解釋被標記成這樣的行:


-*- mode: c -*-


或者這樣的:


/*
Local Variables:
compile-command: "gcc -DMAGIC_DEBUG_FLAG foo.c"
End:
*/


Vim能夠解釋這樣的標記:


/* vim:set sw=8 noet */


不要在原始碼中包含任何這樣的內容。每個人都有他自己的編輯器配置,你的原始檔不應
該覆蓋別人的配置。這包括有關縮排和模式配置的標記。人們可以使用他們自己定製的模
式,或者使用其他可以產生正確的縮排的巧妙方法。






附錄 I:參考


The C Programming Language, 第二版, 作者Brian W. Kernighan和Denni
M. Ritchie. Prentice Hall, Inc., 1988. ISBN 0-13-110362-8 (軟皮),
0-13-110370-9 (硬皮). URL: http://cm.bell-labs.com/cm/cs/cbook/


The Practice of Programming 作者Brian W. Kernighan和Rob Pike.  Addison-Wesley,
Inc., 1999.  ISBN 0-201-61586-X.  URL: http://cm.bell-labs.com/cm/cs/tpop/


cpp,gcc,gcc internals和indent的GNU手冊——和K&R及本文相符合的部分,全部可以在
http://www.gnu.org/manual/找到


WG14是C語言的國際標準化工作組,URL: http://www.open-std.org/JTC1/SC22/WG14/


Kernel CodingStyle,作者
[email protected]
發表於OLS 2002:
http://www.kroah.com/linux/talks/ols_2002_kernel_codingstyle_talk/html/


--
最後更新於2007年7月13日。

相關推薦

unix K&R編碼風格

Chinese translated version of Documentation/CodingStyle If you have any comment or update to the content, please post to LKML directly. H

ANSI和K&R兩種函式定義風格

在C語言的函式定義上,我們通常都是用的函式定義方式為ANSI-C的函式定義方式。但是在C語言之父創立C語言之時,函式的定義形式並非現在我們所見到的形式。下面的程式碼顯示了這樣的差別,注意觀察二者在宣告與函式體簽名上的差別。 #include <stdio.h>

PSR-2 編碼風格規範

rabl val 字符 調用 所有 public 修飾符 class 只有一個 本篇規範是 PSR-1 基本代碼規範的繼承與擴展。 本規範希望通過制定一系列規範化PHP代碼的規則,以減少在瀏覽不同作者的代碼時,因代碼風格的不同而造成不便。 當多名程序員在多個項目中合作時,就

[中英對照]Linux kernel coding style | Linux內核編碼風格

ril views har func Language config req bing evel Linux kernel coding style | Linux內核編碼風格 This is a short document describing the preferre

(轉)PEP 8——Python編碼風格指南

present directory bject xtra == acc making 嵌套 vertica PEP 8——Python編碼風格指南標簽(空格分隔): Python PEP8 編碼規範原文:https://lizhe2004.gitbooks.io/code-

pear 安裝php_codesniffer 和phpstorm設置統一編碼風格

編碼風格 ado href cmd ffffff exe tex 風格 ref 詳細按照參考http://www.cnblogs.com/huangbx/p/php_codesniffer.html pear下載地址http://pear.php.net/go-pearph

Python 編碼風格 ---持續更新

1、類 類名採用駝峰命名法,即類名中每個單詞首字母大寫,而不使用下劃線。例如:建立一個電動車的類 class Car(): ... class ElectricCar(Car): ... 例項名和模組名都採用小寫格式,並在單詞之間加下劃線,例如:

Python:編碼風格

約定程式碼格式有助於讓自己的程式碼易於閱讀,讓程式碼易於閱讀有助於自己掌握程式是做什麼的,也可以幫助他人理解自己編寫的程式碼(編碼風格) 需要同時匯入標準庫中的模組和自己編寫的模組時,先編寫匯入標準庫模組的import語句,再新增一個空行,然後編寫匯入自己編寫的模組的import語句。在

《Python程式設計從入門到實踐》記錄之類編碼風格

遵守一定的編寫規則,能夠使得程式更容易閱讀理解和使用 類編碼風格: 類名使用駝峰命名法,即類名中的每個單詞的首字母都大寫,且不用下劃線。例項名和模組名都採用小寫格式,且在單詞之間加上下劃線 每個類,都應緊跟在類定義後面包含一個文件字串,這些字串簡要描述類的功能

K & R C 2nd Edition Exercise 2-10

/* Exercise 2-10. Rewrite the function lower, which converts upper case letters to lower case, with a conditional expression instea

GooGle c++編碼風格--標頭檔案

本文所學習的GooGle規範下載於: 前言:雖然Google的風格很規範,可是一萬個人有一萬個哈姆雷特,所以結合規範風格,引申出適合自己的風格才是最好的。 1.標頭檔案 1.1#define保護  用於防止某個.h檔案被多重包含,命名格式<PROJ

養成良好編碼風格的重要性

在編寫程式碼時,養成良好的程式碼風格,提高程式碼質量,可以避免很多漏洞,為程式碼的維護和擴充套件提高效率。下面以實際工作中遇到的不良編碼風格引出的問題為例,說明良好程式碼風格的作用: if語句、for語句不管有沒有多個語句,一定要使用花括號括起來。下面的這個例子就是因為if語句的body部

通過eslint統一前端IDE的編碼風格,避免git衝突

問題描述 前端專案組的開發人員IDE沒法統一,VSCode,Webstrom,Vim都有。結果就是git老是因為格式衝突 解決方案 專案根目錄下新增檔案: .eslintrc.js module.exports = { "env": {

k&r課後習題整理——第一章

最近閒的有點無聊,打算重新夯實一下C語言基礎,順便做一做K&R的課後習題 1-1、自己試試 1-2、printf(“\c”)的時候,只剩一個c了,那麼這是為什麼呢? c語言前定義了一些+字母表示平常不能顯示的ASCII字元,除了特定的幾個字母以外,

Java 中一些好的編碼風格

一、前言   在實際工作中,我們所編寫的程式碼主要是給我們自己看的,只是順便讓機器去執行。那麼,可想而知,良好的程式碼編寫風格,將給維護人員帶來很大的便利之處。這篇文章是我工作中的一些總結,也許並不適合你,但值得參考,因為我的很多同事並沒有良好的編碼風格和習慣,每次

ROS的編碼風格和命名約定

參考:http://wiki.ros.org/CppStyleGuide可以使用clang-format來自動格式化(不要理解錯了,formatting)你的程式碼.1、命名規則有以下幾種命名格式:CamelCased:名稱以大寫字母開頭,每個新單詞都有大寫字母,不帶下劃線。

ANSI C 與 K&R C

C語言由Dennis M.Ritchie在1973年設計和實現。從那以後使用者逐漸增加。到1978年Ritchie和Bell實驗室的另一位程式專家Kernighan合寫了著名的《TheC ProgrammingLanguage》,將C語言推向全世界,許多國家都出了譯本,國內有一些C語言書就是這本書的翻譯或者編

面試題-關於良好的編碼風格

//假設布林變數名字為flag,它與零值比較標準if語句 //1.1 if(flag == TRUE) if(flag == FALSE) //1.2 if(flag) if(!f

Python筆記——基本語法:識別符號、資料型別、變數、運算子及表示式/編碼風格

在Python 中,每行程式以換行符代表結束,如果一行程式太長的話,可以用“/”符號擴充套件到下一行。在python中以三引號(""")括起來的字串,列表,元組和字典都能跨行使用。並且以小括號(...)、中括號[...]和大括號{...}包圍的程式碼不用加“/”符也可擴充套件到多行。如:

統一Eclipse的編碼風格

作為一個團隊,如果每個人都使用自己的編碼風格,每次從伺服器更新、提交程式碼後,無疑會增加團隊不必要的程式碼閱讀時間,所以有必要統一團隊的編碼風格。一個成熟的團隊來源於實踐中的經驗積累和成員的磨合。 以Eclipse為例來說: 1、【window】/【preferences】