每個人都有自己的編碼風(fēng)格,我不會將我的觀點強加到任何人的身上,但這正是我所要保持的東西,就像其他許多事情一樣。至少請考慮在這里所列出的觀點。
首先,我建議打印出GNU編碼標(biāo)準(zhǔn)的副本,不要去閱讀,直接將它燒毀。這是一個偉大的象征性的姿態(tài)。
好,現(xiàn)在正式開始:
第1章:縮進(jìn)
T一個Tab鍵有8個字符位因此一個縮進(jìn)也是8個字符位. 有人試圖將一個縮進(jìn)定義場4個字符位甚至2個, 這無異于試圖將Pi的值定義為3.
說明: 縮進(jìn)的意義在于定義語句塊的開始和結(jié)尾.尤其是當(dāng)你緊盯屏幕20小時后你就更加能體會到縮進(jìn)的作用了.
現(xiàn)在有些人說8字符長的縮進(jìn)使得代碼太靠右邊,且很難在80字符的終端窗口閱讀。事實是,如果你的代碼需要三層以上的縮進(jìn),這是你犯糊涂了,你得修改你的程序。
總之,8字符縮進(jìn)會讓程序更好閱讀,而而外的好處則是能夠提醒你你的程序嵌套的太深了。這個需要警惕。
精簡 switch 語句中縮進(jìn)界級別的最好方式就是將"switch"和其下級的"case"放在同一列,而不是雙重縮進(jìn)"case"標(biāo)簽。例如:

不要將多個語句放在同一行,除非你要掩飾什么:

同樣也不要將多個賦值放在同一行。內(nèi)核編程范式超簡單。避免復(fù)雜的表達(dá)式。
還有一個潛規(guī)則,是 Kconfig 推薦的,永遠(yuǎn)不要用空格縮進(jìn),上面的例子是故意的。找一個合適的編輯器,記得不要在行末留有空格。
第二章:換行編程范式的意義在于使用常見工具時的可閱讀和可維護性。行的長度限制為80列,這是強烈推薦的設(shè)置。
多于80列的語句將被分為合適的數(shù)塊。子句應(yīng)該永遠(yuǎn)比主句短,且比主句更靠右側(cè)。這對有較長參數(shù)表的函數(shù)聲明同樣有效。長字符串同樣也被打斷為短字符串。這樣做能在超過80列時提高可閱讀性,且不會隱藏信息。

第三章:大括號和空格
關(guān)于 C 風(fēng)格編程的另一個議點就是大括號。跟縮進(jìn)值不同,大括號的放置并沒有技術(shù)因素在內(nèi),但“先賢“ Kernighan 和 Ritchie 展示為我們的最好方式,是將左大括號放置在行末尾。而右大括號放置在行首,如下:

對非函數(shù)的語句塊也是如此(if、switch、for、while、do)。例如:

當(dāng)然那,有一個特殊的例外, 即函數(shù):它的左大括號在新行的行首,如下:

全世界有不同意見的人都認(rèn)為此不一致的地方——好吧——很是缺乏一致性,不過只要是能正常思考的都知道《C程序設(shè)計語言(第一版)》中是right而《C程序設(shè)計語言(第二版)》中則是 right。無論如何,函數(shù)都是特殊的(在 C 語言中你不能嵌套定義它們)。
注意,右大括號單獨一行,除非后面跟著的是同一個語句。例如 do 語句中的 "while"或 if 語句中的 "else" 如下:

和

闡述:K&R,《C程序設(shè)計語言》一書的縮寫。
同時要注意,這種放置大括號的方式能夠在不影響可讀性的同時,有效減少空行(或近乎空行)的數(shù)量。因此,你不許要刷新資源就可以看倒更多行(想想一下只能顯示25行的終端窗口),且你能夠有空多的空行來安置注釋。
只有一行語句時不用添加多余的大括號。

和

判斷語句中只有一個分支為單行語句是,不支持這樣用,你需要在所有分支中都加入大括號。

3.1:空格
linux內(nèi)核風(fēng)格下的空格,其實際應(yīng)用主要是取決于函數(shù)標(biāo)識符的使用。大多數(shù)函數(shù)在其標(biāo)識符后面會加上空格。當(dāng)然,sizeof, typeof, alignof,和 __attribute__,像這樣的長得像函數(shù)(但是因為不需要,所以經(jīng)常屁股后面不跟著圓括號。例如:"sizeof info"如果在 "struct fileinfo info;"這個聲明之后……木有括號……)的,就不需要加空格了。
因此你就知道,在如下關(guān)鍵字后面加個空格:
if, switch, case, for, do, while
而sizeof, typeof, alignof, or__attribute__后面就免了吧,例如

當(dāng)聲明指針或者聲明一個返回指針的函數(shù)時,最好是在把個小小的“*”離著標(biāo)識符(不論是變量常量還是函數(shù))近一點,而不是和數(shù)據(jù)類型近一點,如:

在雙目或者三目運算符周圍(左邊和右邊)用上空格——他們包括(譯者注:可能不止,并未對此進(jìn)行仔細(xì)檢查):
= + - > * / % | & ^ <=? >= == != ? :
但是單目運算符就算了,比如這么幾個家伙(譯者注:同上,未作檢查):
& * + - ~ ! sizeof typeof alignof __attribute__ defined還有幾個特殊的,比如自增自減運算符,和他們進(jìn)行運算的變量標(biāo)識符中間(譯者注:原文這里是說,前后都不用加空格)不用加空格。就是這兩個家伙:
++ --
然后,“.”和“->”這兩個運算符,加括號是作死的行為,你不這么想么?
別在一行的末尾留幾個空格。一些具有智能縮進(jìn)功能的編輯器會在新行的開頭適當(dāng)?shù)牟迳峡崭?,然后你就可以立即繼續(xù)寫你的程序。但是部分編輯器不會自動刪除你程序每一行末尾的空格(雖然你的程序在那幾個空格之前已經(jīng)結(jié)束了),甚至這會產(chǎn)生一個完全空白的行,期間充斥著空格這種可惡的東西。
Git在這種情況下會對你進(jìn)行警告,并提醒你是否由它來為你消滅這些惱人的小東西;但是這種修補總是比不上你自己進(jìn)行的修補,如果你讓Git干了太多這樣的活,可能導(dǎo)致你程序行的錯亂(所謂進(jìn)退失據(jù))。
第四章: 命名
C 語言是粗獷的,你的命名同樣如此。與 Modula-2 和 Pascal 不同,C 程序員不使用類似 ThisVariableIsATemporaryCounter 這樣有趣的命名。C 程序員會將變量命名為類似 "tmp"的名字,這種名字比較好寫,且不難理解。
當(dāng)然,雖然混合大小寫的難以讓人接受,但對于全局變量,一個描述性的名字卻是必須的。調(diào)用一個名為 "foo" 全局函數(shù)顯然是在找不自在。
全局變量(只有你真正需要的時候,再用它)需要一個描述性的名字,全局函數(shù)也是。如果你有一個計算活躍用戶數(shù)量的函數(shù),那么命名其為 "count_active_users()" 或者類似的名字,而非"cntusr()".
將函數(shù)類型添加到其名字中(即匈牙利命名法)是有害大腦的——編譯器知道、也會檢查它的,這只能混淆程序員。所以微軟才會生產(chǎn)那么多充滿BUG的程序。
局部變量的名字應(yīng)當(dāng)簡潔了當(dāng)。如果在循環(huán)中需要一些隨機數(shù)字,你大可以命名其為 "i" 。只要不會產(chǎn)生歧義,命名為 "loop_counter" 毫無意義。同樣的,"tmp" 可以是任何類型的臨時變量。
如果你擔(dān)心弄混局部變量的名字,那你有另一個問題:函數(shù)增長荷爾蒙失調(diào)綜合癥。
第5章: Typedefs
不要傻呼呼地使用像"vps_t"這樣的變量類型.
使用typedef來重定義已有結(jié)構(gòu)體和指針本身就是個錯誤. 當(dāng)你在源代碼中見到這樣的定義:
vps_t a;
天知道a到底是個什么東西!
如果你看到這樣的定義:
struct virtual_container *a;
你完全可以一目然: 哦, a是一個指向...的指針.
多數(shù)人都覺得typedefs可以提高可閱讀性, 但真理往往掌握在少數(shù)人手中. Typedefs只有在如下情況下有用:
(a) 需要被封裝起來的對象(你本來就打算隱藏起類型信息)
如: "pte_t"這種類型. 封裝出這樣的類型本來就只打算讓特定的"訪問函數(shù)"才能訪問.
請注意: 封裝以及"訪問函數(shù)"本來就不是什么好東西. The reason we have them for things like pte_t etc. is that there really is absolutely _zero_ portably accessible information there.
(b) 定長的整數(shù)類型. 這樣可以在避免在某些情況下, 搞不清楚到底用的是int還是long, 把你自己搞暈!
u8/u16/u32都是完美的typdefs, 盡管更應(yīng)該它們歸結(jié)至規(guī)則(d)下.
再次重申: 這樣定義必須要有合理的理由. 如果某個變量本來就是unsigned long類型, 你硬要它定義成這樣,你就是SB了:
typedef unsigned long myflags_t;
如果你有明確的理由, 在某種情形下變量是unsigned int類型, 而在另外的情形下又要變身成為unsigned long類型, 那就盡管去typedef.
(c) 當(dāng)你需要使用kernel的sparse工具做變量類型檢查時, 你也可以typedef一個類型.
(d)對于特殊情況下的某些c99標(biāo)準(zhǔn)的新類型
你的大腦和眼睛只需要很短的時間就可以習(xí)慣像'uint32_t'這樣的新類型,雖然有的人反對這宗用法。
因此,雖然對于你的新代碼來說,linux獨有的'u8/u16/u32/u64'類型并不是強制的,但是他們也是與標(biāo)準(zhǔn)類型等價的。
當(dāng)編輯現(xiàn)有代碼時,如果其中已經(jīng)使用了某一種類型名規(guī)范,你應(yīng)該遵循原樣,使用與之相同的類型名。
(e)用戶空間中的類型安全
對于某些結(jié)構(gòu),顯然我們不能使用c99標(biāo)準(zhǔn)的類型,不能使用上述的‘u32’,因此咱干脆在結(jié)構(gòu)中使用'_u32'或者類似的類型好了。
也許還有其它情況,但基本規(guī)則是,除非你很清除符合某一條規(guī)則,否則永遠(yuǎn)不要使用typedef。
通常,一個指針或是一個含有元素的結(jié)構(gòu)體,若能直接訪問,永遠(yuǎn)不是typedef。
第6章:函數(shù)
函數(shù)這種東西,應(yīng)該小而精,換句話說,只是專心的做一件事情一直到底。如果你把它赤身裸體的展示于屏幕之上,你應(yīng)該在兩個屏幕(這里一個屏幕的大小根據(jù)ISO/ANSI標(biāo)準(zhǔn)應(yīng)該是80X24的)之內(nèi)就可以看完它。
函數(shù)的長度和縮進(jìn)程度和它的復(fù)雜程度是成反比的。所以,如果你的函數(shù)確實單純(譯者注:是萌妹子那樣的單純不?)而簡單,比如用一個長長的switch-case語句來做點簡簡單單的事情來處理一些單單純純的情況,來吧,咱把函數(shù)寫再長一點。
相反的,如果你的函數(shù)相當(dāng)?shù)膹?fù)雜,復(fù)雜到你嚴(yán)重懷疑一個普普通通的不想你一樣天才的高中學(xué)生看不懂……返工吧,把函數(shù)縮短縮短再縮短,不要猶豫,多造幾個輔助函數(shù)并給他們幾個一看即知的名字,以減少函數(shù)復(fù)雜性。(當(dāng)然,如果你覺得這個函數(shù)太關(guān)鍵了,如果拆開了寫肯定會影響整個程序的性能,那你干脆內(nèi)聯(lián),用那個內(nèi)聯(lián)函數(shù)或者使用編譯器的內(nèi)聯(lián)功能)
規(guī)范對于函數(shù)的另外一個要求是關(guān)于局部變量的個數(shù)的:最多5-10個。就算你是天才,你的腦子一般也就能輕松關(guān)注7個變量,再多了就絕對會出現(xiàn)一些你意想不到的錯誤(誰叫你一心多用來著)。好吧,就算你是天才,如果還想明白你在兩周之前所寫的那些程序的話,還是遵守這些規(guī)范吧。
就源代碼來說,函數(shù)與函數(shù)的原型之間應(yīng)該有一個空白行作為分隔。如果該函數(shù)在其他文件中被引用,那么他的EXPORT*宏應(yīng)當(dāng)和函數(shù)最后一行的那個大括號中間沒有任何空格。例如:

另外,在函數(shù)原型中,當(dāng)聲明參量時應(yīng)該將參量的類型和標(biāo)識符放到一起,雖然c語言本身并不要求這種形式,但是在linux kernal里面是要求的——目的是增加每一行代碼的信息量。
章7:集中于一處退出函數(shù)
雖然很多人不提倡,但是我們這里要經(jīng)常使用goto進(jìn)行無條件的跳轉(zhuǎn)。
當(dāng)函數(shù)有很多個出口,使用goto把這些出口集中到一處是很方便的,特別是函數(shù)中有許多重復(fù)的清理工作的時候。
理由是:
-無條件跳轉(zhuǎn)易于理解
-可以減少嵌套
-可以避免那種忘記更新某一個出口點的問題
-算是幫助編譯器做了代碼優(yōu)化

第8章:注釋
有注釋當(dāng)然是好的,但是注釋太多就很惡心了。千萬不要在注釋里面解釋你的程序怎么運行的。相對于嘗試用注釋解釋清楚你那惡心的代碼,你還不如就寫個清晰易懂(譯者注:就是小而精,萌妹子一樣單純的~)的函數(shù)。
一般的,你的注釋是用來說明這段代碼是“干啥的”,而不是“怎么干的”。另外,別把注釋放到你的函數(shù)體里面(譯者注:把我放到妹子的懷抱里吧?。喝邕^你的函數(shù)確實復(fù)雜到需要用注釋來分隔成幾段,回第六章擦亮眼睛再看兩遍(譯者注:那一段正好也是我翻譯的)。以可以用幾個小注釋來提醒大家一些東西(“寫的好~”或者“寫的真tm糟糕”),但是就不要自取其辱評論自己的東西了……最后,仍然提醒你,一定是把注釋安放在函數(shù)的前面,然后簡單寫下這段函數(shù)式干啥的,如果可能,你倒是不妨提及為什么你要這樣做。
當(dāng)給kernal api函數(shù)注釋的時候,請使用kernal-doc格式。該格式的細(xì)節(jié)你可以參考這兩個文件:Documentation/kernel-doc-nano-HOWTO.txt和scripts/kernel-doc(譯者注:這個文件路徑應(yīng)當(dāng)是指內(nèi)核源碼中的路徑)
linux當(dāng)中的注釋是c89格式("/* ... */")的,而不是c99中新近添加的"http:// ..."
多于一行(多行)的注釋應(yīng)當(dāng)準(zhǔn)從以下格式:

該格式對于注釋標(biāo)識符(常量,變量,函數(shù)等)同樣適用。換句話說,你最好不要再一行里面同時聲明很多個標(biāo)識符(無論是用逗號還是分號隔開都是不推薦的),一行一個就可以了。這樣你就可以在每一行對每一個標(biāo)識符進(jìn)行解釋。
第九章:內(nèi)存分配
內(nèi)核提供了下列通用內(nèi)存分配器:kmalloc()、kzalloc()、kcalloc()、vmalloc()、和 vzalloc()。 更多信息,請參閱的 API 文檔。
傳遞一個結(jié)構(gòu)體大小的最好方式如下:

另外一種傳遞方式中,sizeof的操作數(shù)是結(jié)構(gòu)體的名字,這樣有損可讀性,并會在指針類型改變,但傳遞給內(nèi)存分配器的大小沒有變化時導(dǎo)致BUG。強制轉(zhuǎn)換返回的void指針是冗余的。C語言本身保證了從void指針到其他任何指針類型的轉(zhuǎn)換。
-
Linux
+關(guān)注
關(guān)注
88文章
11822瀏覽量
219600 -
C語言
+關(guān)注
關(guān)注
183文章
7646瀏覽量
146202 -
編輯器
+關(guān)注
關(guān)注
1文章
829瀏覽量
33078
原文標(biāo)題:這是一篇描述 linux 內(nèi)核首選編碼樣式的文檔
文章出處:【微信號:gh_c472c2199c88,微信公眾號:嵌入式微處理器】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
Linux內(nèi)核編碼風(fēng)格(編程代碼風(fēng)格推薦)
S32K3 uC有首選編譯器嗎?
Linux的內(nèi)核教程
linux內(nèi)核rcu機制詳解
linux內(nèi)核是什么_linux內(nèi)核學(xué)習(xí)路線
linux內(nèi)核參數(shù)設(shè)置_linux內(nèi)核的功能有哪些
Linux內(nèi)核的首選代碼風(fēng)格應(yīng)該如何設(shè)置
Linux內(nèi)核GPIO操作函數(shù)的詳解分析
關(guān)于Linux的內(nèi)核代碼風(fēng)格
SRRC證書樣式和代碼編碼規(guī)則即將施行
兆芯正引入Linux首選內(nèi)核調(diào)度技術(shù),優(yōu)化性能
linux內(nèi)核首選編碼樣式的文檔
評論