每個人都有自己的編碼風格,我不會將我的觀點強加到任何人的身上,但這正是我所要保持的東西,就像其他許多事情一樣。至少請考慮在這里所列出的觀點。
首先,我建議打印出GNU編碼標準的副本,不要去閱讀,直接將它燒毀。這是一個偉大的象征性的姿態。
好,現在正式開始:
第1章:縮進
T一個Tab鍵有8個字符位因此一個縮進也是8個字符位. 有人試圖將一個縮進定義場4個字符位甚至2個, 這無異于試圖將Pi的值定義為3.
說明: 縮進的意義在于定義語句塊的開始和結尾.尤其是當你緊盯屏幕20小時后你就更加能體會到縮進的作用了.
現在有些人說8字符長的縮進使得代碼太靠右邊,且很難在80字符的終端窗口閱讀。事實是,如果你的代碼需要三層以上的縮進,這是你犯糊涂了,你得修改你的程序。
總之,8字符縮進會讓程序更好閱讀,而而外的好處則是能夠提醒你你的程序嵌套的太深了。這個需要警惕。
精簡 switch 語句中縮進界級別的最好方式就是將"switch"和其下級的"case"放在同一列,而不是雙重縮進"case"標簽。例如:
不要將多個語句放在同一行,除非你要掩飾什么:
同樣也不要將多個賦值放在同一行。內核編程范式超簡單。避免復雜的表達式。
還有一個潛規則,是 Kconfig 推薦的,永遠不要用空格縮進,上面的例子是故意的。找一個合適的編輯器,記得不要在行末留有空格。
第二章:換行編程范式的意義在于使用常見工具時的可閱讀和可維護性。行的長度限制為80列,這是強烈推薦的設置。
多于80列的語句將被分為合適的數塊。子句應該永遠比主句短,且比主句更靠右側。這對有較長參數表的函數聲明同樣有效。長字符串同樣也被打斷為短字符串。這樣做能在超過80列時提高可閱讀性,且不會隱藏信息。
第三章:大括號和空格
關于 C 風格編程的另一個議點就是大括號。跟縮進值不同,大括號的放置并沒有技術因素在內,但“先賢“ Kernighan 和 Ritchie 展示為我們的最好方式,是將左大括號放置在行末尾。而右大括號放置在行首,如下:
對非函數的語句塊也是如此(if、switch、for、while、do)。例如:
當然那,有一個特殊的例外, 即函數:它的左大括號在新行的行首,如下:
全世界有不同意見的人都認為此不一致的地方——好吧——很是缺乏一致性,不過只要是能正常思考的都知道《C程序設計語言(第一版)》中是right而《C程序設計語言(第二版)》中則是 right。無論如何,函數都是特殊的(在 C 語言中你不能嵌套定義它們)。
注意,右大括號單獨一行,除非后面跟著的是同一個語句。例如 do 語句中的 "while"或 if 語句中的 "else" 如下:
和
闡述:K&R,《C程序設計語言》一書的縮寫。
同時要注意,這種放置大括號的方式能夠在不影響可讀性的同時,有效減少空行(或近乎空行)的數量。因此,你不許要刷新資源就可以看倒更多行(想想一下只能顯示25行的終端窗口),且你能夠有空多的空行來安置注釋。
只有一行語句時不用添加多余的大括號。
和
判斷語句中只有一個分支為單行語句是,不支持這樣用,你需要在所有分支中都加入大括號。
3.1:空格
linux內核風格下的空格,其實際應用主要是取決于函數標識符的使用。大多數函數在其標識符后面會加上空格。當然,sizeof, typeof, alignof,和 __attribute__,像這樣的長得像函數(但是因為不需要,所以經常屁股后面不跟著圓括號。例如:"sizeof info"如果在 "struct fileinfo info;"這個聲明之后……木有括號……)的,就不需要加空格了。
因此你就知道,在如下關鍵字后面加個空格:
if, switch, case, for, do, while
而sizeof, typeof, alignof, or__attribute__后面就免了吧,例如
當聲明指針或者聲明一個返回指針的函數時,最好是在把個小小的“*”離著標識符(不論是變量常量還是函數)近一點,而不是和數據類型近一點,如:
在雙目或者三目運算符周圍(左邊和右邊)用上空格——他們包括(譯者注:可能不止,并未對此進行仔細檢查):
= + - > * / % | & ^ <=? >= == != ? :
但是單目運算符就算了,比如這么幾個家伙(譯者注:同上,未作檢查):
& * + - ~ ! sizeof typeof alignof __attribute__ defined還有幾個特殊的,比如自增自減運算符,和他們進行運算的變量標識符中間(譯者注:原文這里是說,前后都不用加空格)不用加空格。就是這兩個家伙:
++ --
然后,“.”和“->”這兩個運算符,加括號是作死的行為,你不這么想么?
別在一行的末尾留幾個空格。一些具有智能縮進功能的編輯器會在新行的開頭適當的插上空格,然后你就可以立即繼續寫你的程序。但是部分編輯器不會自動刪除你程序每一行末尾的空格(雖然你的程序在那幾個空格之前已經結束了),甚至這會產生一個完全空白的行,期間充斥著空格這種可惡的東西。
Git在這種情況下會對你進行警告,并提醒你是否由它來為你消滅這些惱人的小東西;但是這種修補總是比不上你自己進行的修補,如果你讓Git干了太多這樣的活,可能導致你程序行的錯亂(所謂進退失據)。
第四章: 命名
C 語言是粗獷的,你的命名同樣如此。與 Modula-2 和 Pascal 不同,C 程序員不使用類似 ThisVariableIsATemporaryCounter 這樣有趣的命名。C 程序員會將變量命名為類似 "tmp"的名字,這種名字比較好寫,且不難理解。
當然,雖然混合大小寫的難以讓人接受,但對于全局變量,一個描述性的名字卻是必須的。調用一個名為 "foo" 全局函數顯然是在找不自在。
全局變量(只有你真正需要的時候,再用它)需要一個描述性的名字,全局函數也是。如果你有一個計算活躍用戶數量的函數,那么命名其為 "count_active_users()" 或者類似的名字,而非"cntusr()".
將函數類型添加到其名字中(即匈牙利命名法)是有害大腦的——編譯器知道、也會檢查它的,這只能混淆程序員。所以微軟才會生產那么多充滿BUG的程序。
局部變量的名字應當簡潔了當。如果在循環中需要一些隨機數字,你大可以命名其為 "i" 。只要不會產生歧義,命名為 "loop_counter" 毫無意義。同樣的,"tmp" 可以是任何類型的臨時變量。
如果你擔心弄混局部變量的名字,那你有另一個問題:函數增長荷爾蒙失調綜合癥。
第5章: Typedefs
不要傻呼呼地使用像"vps_t"這樣的變量類型.
使用typedef來重定義已有結構體和指針本身就是個錯誤. 當你在源代碼中見到這樣的定義:
vps_t a;
天知道a到底是個什么東西!
如果你看到這樣的定義:
struct virtual_container *a;
你完全可以一目然: 哦, a是一個指向...的指針.
多數人都覺得typedefs可以提高可閱讀性, 但真理往往掌握在少數人手中. Typedefs只有在如下情況下有用:
(a) 需要被封裝起來的對象(你本來就打算隱藏起類型信息)
如: "pte_t"這種類型. 封裝出這樣的類型本來就只打算讓特定的"訪問函數"才能訪問.
請注意: 封裝以及"訪問函數"本來就不是什么好東西. The reason we have them for things like pte_t etc. is that there really is absolutely _zero_ portably accessible information there.
(b) 定長的整數類型. 這樣可以在避免在某些情況下, 搞不清楚到底用的是int還是long, 把你自己搞暈!
u8/u16/u32都是完美的typdefs, 盡管更應該它們歸結至規則(d)下.
再次重申: 這樣定義必須要有合理的理由. 如果某個變量本來就是unsigned long類型, 你硬要它定義成這樣,你就是SB了:
typedef unsigned long myflags_t;
如果你有明確的理由, 在某種情形下變量是unsigned int類型, 而在另外的情形下又要變身成為unsigned long類型, 那就盡管去typedef.
(c) 當你需要使用kernel的sparse工具做變量類型檢查時, 你也可以typedef一個類型.
(d)對于特殊情況下的某些c99標準的新類型
你的大腦和眼睛只需要很短的時間就可以習慣像'uint32_t'這樣的新類型,雖然有的人反對這宗用法。
因此,雖然對于你的新代碼來說,linux獨有的'u8/u16/u32/u64'類型并不是強制的,但是他們也是與標準類型等價的。
當編輯現有代碼時,如果其中已經使用了某一種類型名規范,你應該遵循原樣,使用與之相同的類型名。
(e)用戶空間中的類型安全
對于某些結構,顯然我們不能使用c99標準的類型,不能使用上述的‘u32’,因此咱干脆在結構中使用'_u32'或者類似的類型好了。
也許還有其它情況,但基本規則是,除非你很清除符合某一條規則,否則永遠不要使用typedef。
通常,一個指針或是一個含有元素的結構體,若能直接訪問,永遠不是typedef。
第6章:函數
函數這種東西,應該小而精,換句話說,只是專心的做一件事情一直到底。如果你把它赤身裸體的展示于屏幕之上,你應該在兩個屏幕(這里一個屏幕的大小根據ISO/ANSI標準應該是80X24的)之內就可以看完它。
函數的長度和縮進程度和它的復雜程度是成反比的。所以,如果你的函數確實單純(譯者注:是萌妹子那樣的單純不?)而簡單,比如用一個長長的switch-case語句來做點簡簡單單的事情來處理一些單單純純的情況,來吧,咱把函數寫再長一點。
相反的,如果你的函數相當的復雜,復雜到你嚴重懷疑一個普普通通的不想你一樣天才的高中學生看不懂……返工吧,把函數縮短縮短再縮短,不要猶豫,多造幾個輔助函數并給他們幾個一看即知的名字,以減少函數復雜性。(當然,如果你覺得這個函數太關鍵了,如果拆開了寫肯定會影響整個程序的性能,那你干脆內聯,用那個內聯函數或者使用編譯器的內聯功能)
規范對于函數的另外一個要求是關于局部變量的個數的:最多5-10個。就算你是天才,你的腦子一般也就能輕松關注7個變量,再多了就絕對會出現一些你意想不到的錯誤(誰叫你一心多用來著)。好吧,就算你是天才,如果還想明白你在兩周之前所寫的那些程序的話,還是遵守這些規范吧。
就源代碼來說,函數與函數的原型之間應該有一個空白行作為分隔。如果該函數在其他文件中被引用,那么他的EXPORT*宏應當和函數最后一行的那個大括號中間沒有任何空格。例如:
另外,在函數原型中,當聲明參量時應該將參量的類型和標識符放到一起,雖然c語言本身并不要求這種形式,但是在linux kernal里面是要求的——目的是增加每一行代碼的信息量。
章7:集中于一處退出函數
雖然很多人不提倡,但是我們這里要經常使用goto進行無條件的跳轉。
當函數有很多個出口,使用goto把這些出口集中到一處是很方便的,特別是函數中有許多重復的清理工作的時候。
理由是:
-無條件跳轉易于理解
-可以減少嵌套
-可以避免那種忘記更新某一個出口點的問題
-算是幫助編譯器做了代碼優化
第8章:注釋
有注釋當然是好的,但是注釋太多就很惡心了。千萬不要在注釋里面解釋你的程序怎么運行的。相對于嘗試用注釋解釋清楚你那惡心的代碼,你還不如就寫個清晰易懂(譯者注:就是小而精,萌妹子一樣單純的~)的函數。
一般的,你的注釋是用來說明這段代碼是“干啥的”,而不是“怎么干的”。另外,別把注釋放到你的函數體里面(譯者注:把我放到妹子的懷抱里吧!):如過你的函數確實復雜到需要用注釋來分隔成幾段,回第六章擦亮眼睛再看兩遍(譯者注:那一段正好也是我翻譯的)。以可以用幾個小注釋來提醒大家一些東西(“寫的好~”或者“寫的真tm糟糕”),但是就不要自取其辱評論自己的東西了……最后,仍然提醒你,一定是把注釋安放在函數的前面,然后簡單寫下這段函數式干啥的,如果可能,你倒是不妨提及為什么你要這樣做。
當給kernal api函數注釋的時候,請使用kernal-doc格式。該格式的細節你可以參考這兩個文件:Documentation/kernel-doc-nano-HOWTO.txt和scripts/kernel-doc(譯者注:這個文件路徑應當是指內核源碼中的路徑)
linux當中的注釋是c89格式("/* ... */")的,而不是c99中新近添加的"http:// ..."
多于一行(多行)的注釋應當準從以下格式:
該格式對于注釋標識符(常量,變量,函數等)同樣適用。換句話說,你最好不要再一行里面同時聲明很多個標識符(無論是用逗號還是分號隔開都是不推薦的),一行一個就可以了。這樣你就可以在每一行對每一個標識符進行解釋。
第九章:內存分配
內核提供了下列通用內存分配器:kmalloc()、kzalloc()、kcalloc()、vmalloc()、和 vzalloc()。 更多信息,請參閱的 API 文檔。
傳遞一個結構體大小的最好方式如下:
另外一種傳遞方式中,sizeof的操作數是結構體的名字,這樣有損可讀性,并會在指針類型改變,但傳遞給內存分配器的大小沒有變化時導致BUG。強制轉換返回的void指針是冗余的。C語言本身保證了從void指針到其他任何指針類型的轉換。
-
Linux
+關注
關注
87文章
11306瀏覽量
209572 -
C語言
+關注
關注
180文章
7605瀏覽量
136887 -
編輯器
+關注
關注
1文章
806瀏覽量
31178
原文標題:這是一篇描述 linux 內核首選編碼樣式的文檔
文章出處:【微信號:gh_c472c2199c88,微信公眾號:嵌入式微處理器】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
相關推薦
評論