作為程序開發(fā)者,避免不了閱讀別人代碼,那么就會(huì)涉及到到一門語(yǔ)言的編程規(guī)范。規(guī)范雖然不是語(yǔ)言本身的硬性要求,但是已經(jīng)是每一個(gè)語(yǔ)言使用者約定俗成的一個(gè)規(guī)范。
按照編程規(guī)范編寫的代碼,至少在代碼閱讀時(shí),給人一種愉悅的心情,特別是強(qiáng)迫癥患者。另一方面,統(tǒng)一的編程風(fēng)格,可以減少編寫錯(cuò)誤,利于后期維護(hù)。
因?yàn)樽罱珠_始進(jìn)行純C語(yǔ)言的開發(fā),并且是基于SDK的開發(fā),所以添加的每一行代碼都應(yīng)該與原來風(fēng)格保持一致,不能因?yàn)橐活w老鼠屎壞了一鍋湯。一個(gè)良好的編程規(guī)范也可以看出編程人員的細(xì)心程度與代碼質(zhì)量。
之前待過的兩家公司,也都有各自總結(jié)的編程規(guī)范,但都不約而同的一致,適用本公司的軟件開發(fā)。這幾天有幸可以參閱華為技術(shù)有限公司的C語(yǔ)言編程規(guī)范,相比之下,寫的更加詳細(xì)。
至少接觸到了,在這個(gè)編程規(guī)范中體現(xiàn)了,并且還擴(kuò)充了很多,我覺得有必要?dú)w納總結(jié),一遍日后查閱。先是學(xué)習(xí)規(guī)范,然后再積累規(guī)范,最后才是依規(guī)范編寫。
1、清晰第一
清晰性是易于維護(hù)、易于重構(gòu)的程序必需具備的特征。代碼首先是給人讀的,好的代碼應(yīng)當(dāng)可以像文章一樣發(fā)聲朗誦出來。
2.、簡(jiǎn)潔為美
簡(jiǎn)潔就是易于理解并且易于實(shí)現(xiàn)。代碼越長(zhǎng)越難以看懂,也就越容易在修改時(shí)引入錯(cuò)誤。寫的代碼越多,意味著出錯(cuò)的地方越多,也就意味著代碼的可靠性越低。因此,我們提倡大家通過編寫簡(jiǎn)潔明了的代碼來提升代碼可靠性。廢棄的代碼(沒有被調(diào)用的函數(shù)和全局變量)要及時(shí)清除,重復(fù)代碼應(yīng)該盡可能提煉成函數(shù)。
3、選擇合適的風(fēng)格,與代碼原有的風(fēng)格保持一致
產(chǎn)品所有人共同分享同一種風(fēng)格所帶來的好處,遠(yuǎn)遠(yuǎn)超出為了統(tǒng)一而付出的代價(jià)。在公司已有編碼規(guī)范的指導(dǎo)下,審慎地編排代碼以使代碼盡可能清晰,是一項(xiàng)非常重要的技能。如果重構(gòu)/修改其他風(fēng)格的代碼時(shí),比較明智的做法是根據(jù)現(xiàn)有代碼的現(xiàn)有風(fēng)格繼續(xù)編寫代碼,或者使用格式轉(zhuǎn)換工具進(jìn)行轉(zhuǎn)換成公司內(nèi)部風(fēng)格。
一、頭文件
原則1.1 頭文件中適合放置接口的聲明,不適合放置實(shí)現(xiàn)。
說明:頭文件是模塊(Module)或單元(Unit)的對(duì)外接口。頭文件中應(yīng)放置對(duì)外部的聲明,如對(duì)外提供的函數(shù)聲明、宏定義、類型定義等。
原則1.2 頭文件應(yīng)當(dāng)職責(zé)單一。
說明:頭文件過于復(fù)雜,依賴過于復(fù)雜是導(dǎo)致編譯時(shí)間過長(zhǎng)的主要原因。很多現(xiàn)有代碼中頭文件過大,職責(zé)過多,再加上循環(huán)依賴的問題,可能導(dǎo)致為了在.c中使用一個(gè)宏,而包含十幾個(gè)頭文件。
原則1.3 頭文件應(yīng)向穩(wěn)定的方向包含。
說明:頭文件的包含關(guān)系是一種依賴,一般來說,應(yīng)當(dāng)讓不穩(wěn)定的模塊依賴穩(wěn)定的模塊,從而當(dāng)不穩(wěn)定的模塊發(fā)生變化時(shí),不會(huì)影響(編譯)穩(wěn)定的模塊。
規(guī)則1.1 每一個(gè).c文件應(yīng)有一個(gè)同名.h文件,用于聲明需要對(duì)外公開的接口。
說明:如果一個(gè).c文件不需要對(duì)外公布任何接口,則其就不應(yīng)當(dāng)存在,除非它是程序的入口,如main函數(shù)所在的文件。
規(guī)則1.2 禁止頭文件循環(huán)依賴。
說明:頭文件循環(huán)依賴,指a.h包含b.h,b.h包含c.h,c.h包含a.h之類導(dǎo)致任何一個(gè)頭文件修改,都導(dǎo)致所有包含了a.h/b.h/c.h的代碼全部重新編譯一遍。
而如果是單向依賴,如a.h包含b.h,b.h包含c.h,而c.h不包含任何頭文件,則修改a.h不會(huì)導(dǎo)致包含了b.h/c.h的源代碼重新編譯。
規(guī)則1.3 .c/.h文件禁止包含用不到的頭文件。
說明:很多系統(tǒng)中頭文件包含關(guān)系復(fù)雜,開發(fā)人員為了省事起見,可能不會(huì)去一一鉆研,直接包含一切想到的頭文件,甚至有些產(chǎn)品干脆發(fā)布了一個(gè)god.h,其中包含了所有頭文件,然后發(fā)布給各個(gè)項(xiàng)目組使用,這種只圖一時(shí)省事的做法,導(dǎo)致整個(gè)系統(tǒng)的編譯時(shí)間進(jìn)一步惡化,并對(duì)后來人的維護(hù)造成了巨大的麻煩。
規(guī)則1.4 頭文件應(yīng)當(dāng)自包含。
說明:簡(jiǎn)單的說,自包含就是任意一個(gè)頭文件均可獨(dú)立編譯。如果一個(gè)文件包含某個(gè)頭文件,還要包含另外一個(gè)頭文件才能工作的話,就會(huì)增加交流障礙,給這個(gè)頭文件的用戶增添不必要的負(fù)擔(dān)。
規(guī)則1.5 總是編寫內(nèi)部#include保護(hù)符(#define 保護(hù))。
說明:多次包含一個(gè)頭文件可以通過認(rèn)真的設(shè)計(jì)來避免。如果不能做到這一點(diǎn),就需要采取阻止頭文件內(nèi)容被包含多于一次的機(jī)制。
注: 沒有在宏最前面加上 _ ,即使用 FILENAME_H代替 ?FILENAME_H ,是因?yàn)橐话阋?_ 和 ?__ 開頭的標(biāo)識(shí)符為系統(tǒng)保留或者標(biāo)準(zhǔn)庫(kù)使用,在有些靜態(tài)檢查工具中,若全局可見的標(biāo)識(shí)符以 _ 開頭會(huì)給出告警。
定義包含保護(hù)符時(shí),應(yīng)該遵守如下規(guī)則:
1)保護(hù)符使用唯一名稱;
2)不要在受保護(hù)部分的前后放置代碼或者注釋。
規(guī)則1.6 禁止在頭文件中定義變量。
說明:在頭文件中定義變量,將會(huì)由于頭文件被其他.c文件包含而導(dǎo)致變量重復(fù)定義。
規(guī)則1.7 只能通過包含頭文件的方式使用其他.c提供的接口,禁止在.c中通過extern的方式使用外部函數(shù)接口、變量。
說明:若a.c使用了b.c定義的foo()函數(shù),則應(yīng)當(dāng)在b.h中聲明extern int foo(int input);并在a.c中通過#include
規(guī)則1.8 禁止在extern "C"中包含頭文件。
說明:在extern "C"中包含頭文件,會(huì)導(dǎo)致extern "C"嵌套,Visual Studio對(duì)extern "C"嵌套層次有限制,嵌套層次太多會(huì)編譯錯(cuò)誤。
建議1.1 一個(gè)模塊通常包含多個(gè).c文件,建議放在同一個(gè)目錄下,目錄名即為模塊名。為方便外部使用者,建議每一個(gè)模塊提供一個(gè).h,文件名為目錄名。
建議1.2 如果一個(gè)模塊包含多個(gè)子模塊,則建議每一個(gè)子模塊提供一個(gè)對(duì)外的.h,文件名為子模塊名。
建議1.3 頭文件不要使用非習(xí)慣用法的擴(kuò)展名,如.inc。
建議1.4 同一產(chǎn)品統(tǒng)一包含頭文件排列方式。
二、函數(shù)
原則2.1 一個(gè)函數(shù)僅完成一件功能。
說明:一個(gè)函數(shù)實(shí)現(xiàn)多個(gè)功能給開發(fā)、使用、維護(hù)都帶來很大的困難。
原則2.2 重復(fù)代碼應(yīng)該盡可能提煉成函數(shù)。
說明:重復(fù)代碼提煉成函數(shù)可以帶來維護(hù)成本的降低。
規(guī)則2.1 避免函數(shù)過長(zhǎng),新增函數(shù)不超過50行(非空非注釋行)。
說明:本規(guī)則僅對(duì)新增函數(shù)做要求,對(duì)已有函數(shù)修改時(shí),建議不增加代碼行。
規(guī)則2.2 避免函數(shù)的代碼塊嵌套過深,新增函數(shù)的代碼塊嵌套不超過4層。
說明:本規(guī)則僅對(duì)新增函數(shù)做要求,對(duì)已有的代碼建議不增加嵌套層次。
規(guī)則2.3 可重入函數(shù)應(yīng)避免使用共享變量;若需要使用,則應(yīng)通過互斥手段(關(guān)中斷、信號(hào)量)對(duì)其加以保護(hù)。
規(guī)則2.4 對(duì)參數(shù)的合法性檢查,由調(diào)用者負(fù)責(zé)還是由接口函數(shù)負(fù)責(zé),應(yīng)在項(xiàng)目組/模塊內(nèi)應(yīng)統(tǒng)一規(guī)定。缺省由調(diào)用者負(fù)責(zé)。
規(guī)則2.5 對(duì)函數(shù)的錯(cuò)誤返回碼要全面處理。
規(guī)則2.6 設(shè)計(jì)高扇入,合理扇出(小于7)的函數(shù)。
說明:扇出是指一個(gè)函數(shù)直接調(diào)用(控制)其它函數(shù)的數(shù)目,而扇入是指有多少上級(jí)函數(shù)調(diào)用它。如下圖:
img
規(guī)則2.7 廢棄代碼(沒有被調(diào)用的函數(shù)和變量)要及時(shí)清除。
建議2.1 函數(shù)不變參數(shù)使用const。
說明:不變的值更易于理解/跟蹤和分析,把const作為默認(rèn)選項(xiàng),在編譯時(shí)會(huì)對(duì)其進(jìn)行檢查,使代碼更牢固/更安全。
建議2.2 函數(shù)應(yīng)避免使用全局變量、靜態(tài)局部變量和I/O操作,不可避免的地方應(yīng)集中使用。
建議2.3 檢查函數(shù)所有非參數(shù)輸入的有效性,如數(shù)據(jù)文件、公共變量等。
說明:函數(shù)的輸入主要有兩種:一種是參數(shù)輸入;另一種是全局變量、數(shù)據(jù)文件的輸入,即非參數(shù)輸入。函數(shù)在使用輸入?yún)?shù)之前,應(yīng)進(jìn)行有效性檢查。
建議2.4 函數(shù)的參數(shù)個(gè)數(shù)不超過5個(gè)。
建議2.5 除打印類函數(shù)外,不要使用可變長(zhǎng)參函數(shù)。
建議2.6 在源文件范圍內(nèi)聲明和定義的所有函數(shù),除非外部可見,否則應(yīng)該增加static關(guān)鍵字。
三、 標(biāo)識(shí)符命名與定義
目前比較常用的如下幾種命名風(fēng)格:
unix like風(fēng)格:單詞用小寫字母,每個(gè)單詞直接用下劃線_分割,,例如text_mutex,kernel_text_address。
Windows風(fēng)格:大小寫字母混用,單詞連在一起,每個(gè)單詞首字母大寫。不過Windows風(fēng)格如果遇到大寫專有用語(yǔ)時(shí)會(huì)有些別扭,例如命名一個(gè)讀取RFC文本的函數(shù),命令為ReadRFCText,看起來就沒有unix like的read_rfc_text清晰了。
原則3.1 標(biāo)識(shí)符的命名要清晰、明了,有明確含義,同時(shí)使用完整的單詞或大家基本可以理解的縮寫,避免使人產(chǎn)生誤解。
原則3.2 除了常見的通用縮寫以外,不使用單詞縮寫,不得使用漢語(yǔ)拼音。
建議3.1 產(chǎn)品/項(xiàng)目組內(nèi)部應(yīng)保持統(tǒng)一的命名風(fēng)格。
建議3.2 盡量避免名字中出現(xiàn)數(shù)字編號(hào),除非邏輯上的確需要編號(hào)。
建議3.3 標(biāo)識(shí)符前不應(yīng)添加模塊、項(xiàng)目、產(chǎn)品、部門的名稱作為前綴。
建議3.4 平臺(tái)/驅(qū)動(dòng)等適配代碼的標(biāo)識(shí)符命名風(fēng)格保持和平臺(tái)/驅(qū)動(dòng)一致。
建議3.5 重構(gòu)/修改部分代碼時(shí),應(yīng)保持和原有代碼的命名風(fēng)格一致。
建議3.6 文件命名統(tǒng)一采用小寫字符。
規(guī)則3.2 全局變量應(yīng)增加“g_”前綴。
規(guī)則3.3 靜態(tài)變量應(yīng)增加“s_”前綴。
規(guī)則3.4 禁止使用單字節(jié)命名變量,但允許定義i、j、k作為局部循環(huán)變量。
建議3.7 不建議使用匈牙利命名法。
說明:變量命名需要說明的是變量的含義,而不是變量的類型。在變量命名前增加類型說明,反而降低了變量的可讀性;更麻煩的問題是,如果修改了變量的類型定義,那么所有使用該變量的地方都需要修改。
建議3.8 使用名詞或者形容詞+名詞方式命名變量。
建議3.9 函數(shù)命名應(yīng)以函數(shù)要執(zhí)行的動(dòng)作命名,一般采用動(dòng)詞或者動(dòng)詞+名詞的結(jié)構(gòu)。
建議3.10 函數(shù)指針除了前綴,其他按照函數(shù)的命名規(guī)則命名。
規(guī)則3.5 對(duì)于數(shù)值或者字符串等等常量的定義,建議采用全大寫字母,單詞之間加下劃線?_?的方式命名(枚舉同樣建議使用此方式定義)。
規(guī)則3.6 除了頭文件或編譯開關(guān)等特殊標(biāo)識(shí)定義,宏定義不能使用下劃線?_?開頭和結(jié)尾。
四、變量
原則4.1 一個(gè)變量只有一個(gè)功能,不能把一個(gè)變量用作多種用途。
原則4.2 結(jié)構(gòu)功能單一;不要設(shè)計(jì)面面俱到的數(shù)據(jù)結(jié)構(gòu)。
原則4.3 不用或者少用全局變量。
規(guī)則4.1 防止局部變量與全局變量同名。
規(guī)則4.2 通訊過程中使用的結(jié)構(gòu),必須注意字節(jié)序。
規(guī)則4.3 嚴(yán)禁使用未經(jīng)初始化的變量作為右值。
建議4.1 構(gòu)造僅有一個(gè)模塊或函數(shù)可以修改、創(chuàng)建,而其余有關(guān)模塊或函數(shù)只訪問的全局變量,防止多個(gè)不同模塊或函數(shù)都可以修改、創(chuàng)建同一全局變量的現(xiàn)象。
建議4.2 使用面向接口編程思想,通過API訪問數(shù)據(jù):如果本模塊的數(shù)據(jù)需要對(duì)外部模塊開放,應(yīng)提供接口函數(shù)來設(shè)置、獲取,同時(shí)注意全局?jǐn)?shù)據(jù)的訪問互斥。
建議4.3 在首次使用前初始化變量,初始化的地方離使用的地方越近越好。
建議4.4 明確全局變量的初始化順序,避免跨模塊的初始化依賴。
說明:系統(tǒng)啟動(dòng)階段,使用全局變量前,要考慮到該全局變量在什么時(shí)候初始化,使用全局變量和初始化全局變量,兩者之間的時(shí)序關(guān)系,誰(shuí)先誰(shuí)后,一定要分析清楚,不然后果往往是低級(jí)而又災(zāi)難性的。
建議4.5 盡量減少?zèng)]有必要的數(shù)據(jù)類型默認(rèn)轉(zhuǎn)換與強(qiáng)制轉(zhuǎn)換。
說明:當(dāng)進(jìn)行數(shù)據(jù)類型強(qiáng)制轉(zhuǎn)換時(shí),其數(shù)據(jù)的意義、轉(zhuǎn)換后的取值等都有可能發(fā)生變化,而這些細(xì)節(jié)若考慮不周,就很有可能留下隱患。
五、 宏、常量
規(guī)則5.1 用宏定義表達(dá)式時(shí),要使用完備的括號(hào)。
說明:因?yàn)楹曛皇呛?jiǎn)單的代碼替換,不會(huì)像函數(shù)一樣先將參數(shù)計(jì)算后,再傳遞。
規(guī)則5.2 將宏所定義的多條表達(dá)式放在大括號(hào)中。
說明:更好的方法是多條語(yǔ)句寫成do while(0)的方式。
規(guī)則5.3 使用宏時(shí),不允許參數(shù)發(fā)生變化。
規(guī)則5.4 不允許直接使用魔鬼數(shù)字。
說明:使用魔鬼數(shù)字的弊端:代碼難以理解;如果一個(gè)有含義的數(shù)字多處使用,一旦需要修改這個(gè)數(shù)值,代價(jià)慘重。使用明確的物理狀態(tài)或物理意義的名稱能增加信息,并能提供單一的維護(hù)點(diǎn)。
建議5.1 除非必要,應(yīng)盡可能使用函數(shù)代替宏。
說明:宏對(duì)比函數(shù),有一些明顯的缺點(diǎn):宏缺乏類型檢查,不如函數(shù)調(diào)用檢查嚴(yán)格。
建議5.2 常量建議使用const定義代替宏。
建議5.3 宏定義中盡量不使用return、goto、continue、break等改變程序流程的語(yǔ)句。
六、質(zhì)量保證
原則6.1 代碼質(zhì)量保證優(yōu)先原則
(1)正確性,指程序要實(shí)現(xiàn)設(shè)計(jì)要求的功能。
(2)簡(jiǎn)潔性,指程序易于理解并且易于實(shí)現(xiàn)。
(3)可維護(hù)性,指程序被修改的能力,包括糾錯(cuò)、改進(jìn)、新需求或功能規(guī)格變化的適應(yīng)能力。
(4)可靠性,指程序在給定時(shí)間間隔和環(huán)境條件下,按設(shè)計(jì)要求成功運(yùn)行程序的概率。
(5)代碼可測(cè)試性,指軟件發(fā)現(xiàn)故障并隔離、定位故障的能力,以及在一定的時(shí)間和成本前提下,進(jìn)行測(cè)試設(shè)計(jì)、測(cè)試執(zhí)行的能力。
(6)代碼性能高效,指是盡可能少地占用系統(tǒng)資源,包括內(nèi)存和執(zhí)行時(shí)間。
(7)可移植性,指為了在原來設(shè)計(jì)的特定環(huán)境之外運(yùn)行,對(duì)系統(tǒng)進(jìn)行修改的能力。
(8)個(gè)人表達(dá)方式/個(gè)人方便性,指?jìng)€(gè)人編程習(xí)慣。
原則6.2 要時(shí)刻注意易混淆的操作符。比如說一些符號(hào)特性、計(jì)算優(yōu)先級(jí)。
原則6.3 必須了解編譯系統(tǒng)的內(nèi)存分配方式,特別是編譯系統(tǒng)對(duì)不同類型的變量的內(nèi)存分配規(guī)則,如局部變量在何處分配、靜態(tài)變量在何處分配等。
原則6.4 不僅關(guān)注接口,同樣要關(guān)注實(shí)現(xiàn)。
說明:這個(gè)原則看似和“面向接口”編程思想相悖,但是實(shí)現(xiàn)往往會(huì)影響接口,函數(shù)所能實(shí)現(xiàn)的功能,除了和調(diào)用者傳遞的參數(shù)相關(guān),往往還受制于其他隱含約束,如:物理內(nèi)存的限制,網(wǎng)絡(luò)狀況,具體看“抽象漏洞原則”。
規(guī)則6.1 禁止內(nèi)存操作越界。
堅(jiān)持下列措施可以避免內(nèi)存越界:
數(shù)組的大小要考慮最大情況,避免數(shù)組分配空間不夠。
避免使用危險(xiǎn)函數(shù)sprintf /vsprintf/strcpy/strcat/gets操作字符串,使用相對(duì)安全的函數(shù)snprintf/strncpy/strncat/fgets代替。
使用memcpy/memset時(shí)一定要確保長(zhǎng)度不要越界
字符串考慮最后的’’, 確保所有字符串是以’’結(jié)束
指針加減操作時(shí),考慮指針類型長(zhǎng)度
數(shù)組下標(biāo)進(jìn)行檢查
使用時(shí)sizeof或者strlen計(jì)算結(jié)構(gòu)/字符串長(zhǎng)度,,避免手工計(jì)算
堅(jiān)持下列措施可以避免內(nèi)存泄漏:
異常出口處檢查內(nèi)存、定時(shí)器/文件句柄/Socket/隊(duì)列/信號(hào)量/GUI等資源是否全部釋放
刪除結(jié)構(gòu)指針時(shí),必須從底層向上層順序刪除
使用指針數(shù)組時(shí),確保在釋放數(shù)組時(shí),數(shù)組中的每個(gè)元素指針是否已經(jīng)提前被釋放了
避免重復(fù)分配內(nèi)存
小心使用有return、break語(yǔ)句的宏,確保前面資源已經(jīng)釋放
檢查隊(duì)列中每個(gè)成員是否釋放
規(guī)則6.3 禁止引用已經(jīng)釋放的內(nèi)存空間。
堅(jiān)持下列措施可以避免引用已經(jīng)釋放的內(nèi)存空間:
內(nèi)存釋放后,把指針置為NULL;使用內(nèi)存指針前進(jìn)行非空判斷。
耦合度較強(qiáng)的模塊互相調(diào)用時(shí),一定要仔細(xì)考慮其調(diào)用關(guān)系,防止已經(jīng)刪除的對(duì)象被再次使用。
避免操作已發(fā)送消息的內(nèi)存。
自動(dòng)存儲(chǔ)對(duì)象的地址不應(yīng)賦值給其他的在第一個(gè)對(duì)象已經(jīng)停止存在后仍然保持的對(duì)象(具有更大作用域的對(duì)象或者靜態(tài)對(duì)象或者從一個(gè)函數(shù)返回的對(duì)象)
規(guī)則6.4 編程時(shí),要防止差1錯(cuò)誤。
說明:此類錯(cuò)誤一般是由于把“<=”誤寫成“<”或“>=”誤寫成“>”等造成的,由此引起的后果,很多情況下是很嚴(yán)重的,所以編程時(shí),一定要在這些地方小心。當(dāng)編完程序后,應(yīng)對(duì)這些操作符進(jìn)行徹底檢查。使用變量時(shí)要注意其邊界值的情況。
建議6.1 函數(shù)中分配的內(nèi)存,在函數(shù)退出之前要釋放。
說明:有很多函數(shù)申請(qǐng)內(nèi)存,,保存在數(shù)據(jù)結(jié)構(gòu)中,要在申請(qǐng)?zhí)幖由献⑨專f明在何處釋放。
建議6.2 if語(yǔ)句盡量加上else分支,對(duì)沒有else分支的語(yǔ)句要小心對(duì)待。
建議6.3 不要濫用goto語(yǔ)句。
說明:goto語(yǔ)句會(huì)破壞程序的結(jié)構(gòu)性,所以除非確實(shí)需要,最好不使用goto語(yǔ)句。
建議6.4 時(shí)刻注意表達(dá)式是否會(huì)上溢、下溢。
七、 程序效率
原則7.1 在保證軟件系統(tǒng)的正確性、簡(jiǎn)潔、可維護(hù)性、可靠性及可測(cè)性的前提下,提高代碼效率。
原則7.2 通過對(duì)數(shù)據(jù)結(jié)構(gòu)、程序算法的優(yōu)化來提高效率。
建議7.1 將不變條件的計(jì)算移到循環(huán)體外。
建議7.2 對(duì)于多維大數(shù)組,避免來回跳躍式訪問數(shù)組成員。
建議7.3 創(chuàng)建資源庫(kù),以減少分配對(duì)象的開銷。
建議7.4 將多次被調(diào)用的 “小函數(shù)”改為inline函數(shù)或者宏實(shí)現(xiàn)。
八、 注釋
原則8.1 優(yōu)秀的代碼可以自我解釋,不通過注釋即可輕易讀懂。
說明:優(yōu)秀的代碼不寫注釋也可輕易讀懂,注釋無法把糟糕的代碼變好,需要很多注釋來解釋的代碼往往存在壞味道,需要重構(gòu)。
原則8.2 注釋的內(nèi)容要清楚、明了,含義準(zhǔn)確,防止注釋二義性。
原則8.3 在代碼的功能、意圖層次上進(jìn)行注釋,即注釋解釋代碼難以直接表達(dá)的意圖,而不是重復(fù)描述代碼。
規(guī)則8.1 修改代碼時(shí),維護(hù)代碼周邊的所有注釋,以保證注釋與代碼的一致性。不再有用的注釋要?jiǎng)h除。
規(guī)則8.2 文件頭部應(yīng)進(jìn)行注釋,注釋必須列出:版權(quán)說明、版本號(hào)、生成日期、作者姓名、工號(hào)、內(nèi)容、功能說明、與其它文件的關(guān)系、修改日志等,頭文件的注釋中還應(yīng)有函數(shù)功能簡(jiǎn)要說明。
規(guī)則8.3 函數(shù)聲明處注釋描述函數(shù)功能、性能及用法,包括輸入和輸出參數(shù)、函數(shù)返回值、可重入的要求等;定義處詳細(xì)描述函數(shù)功能和實(shí)現(xiàn)要點(diǎn),如實(shí)現(xiàn)的簡(jiǎn)要步驟、實(shí)現(xiàn)的理由、設(shè)計(jì)約束等。
規(guī)則8.4 全局變量要有較詳細(xì)的注釋,包括對(duì)其功能、取值范圍以及存取時(shí)注意事項(xiàng)等的說明。
規(guī)則8.5 注釋應(yīng)放在其代碼上方相鄰位置或右方,不可放在下面。如放于上方則需與其上面的代碼用空行隔開,且與下方代碼縮進(jìn)相同。
規(guī)則8.6 對(duì)于switch語(yǔ)句下的case語(yǔ)句,如果因?yàn)樘厥馇闆r需要處理完一個(gè)case后進(jìn)入下一個(gè)case處理,必須在該case語(yǔ)句處理完、下一個(gè)case語(yǔ)句前加上明確的注釋。
規(guī)則8.7 避免在注釋中使用縮寫,除非是業(yè)界通用或子系統(tǒng)內(nèi)標(biāo)準(zhǔn)化的縮寫。
規(guī)則8.8 同一產(chǎn)品或項(xiàng)目組統(tǒng)一注釋風(fēng)格。
建議8.1 避免在一行代碼或表達(dá)式的中間插入注釋。
建議8.2 注釋應(yīng)考慮程序易讀及外觀排版的因素,使用的語(yǔ)言若是中、英兼有的,建議多使用中文,除非能用非常流利準(zhǔn)確的英文表達(dá)。對(duì)于有外籍員工的,由產(chǎn)品確定注釋語(yǔ)言。
建議8.3 文件頭、函數(shù)頭、全局常量變量、類型定義的注釋格式采用工具可識(shí)別的格式。
說明:采用工具可識(shí)別的注釋格式,例如doxygen格式,方便工具導(dǎo)出注釋形成幫助文檔。
九、 排版與格式
規(guī)則9.1 程序塊采用縮進(jìn)風(fēng)格編寫,每級(jí)縮進(jìn)為4個(gè)空格。
說明:當(dāng)前各種編輯器/IDE都支持TAB鍵自動(dòng)轉(zhuǎn)空格輸入,需要打開相關(guān)功能并設(shè)置相關(guān)功能。編輯器/IDE如果有顯示TAB的功能也應(yīng)該打開,方便及時(shí)糾正輸入錯(cuò)誤。
規(guī)則9.2 相對(duì)獨(dú)立的程序塊之間、變量說明之后必須加空行。
規(guī)則9.3 一條語(yǔ)句不能過長(zhǎng),如不能拆分需要分行寫。一行到底多少字符換行比較合適,產(chǎn)品可以自行確定。
換行時(shí)有如下建議:
換行時(shí)要增加一級(jí)縮進(jìn),使代碼可讀性更好;
低優(yōu)先級(jí)操作符處劃分新行;換行時(shí)操作符應(yīng)該也放下來,放在新行首;
換行時(shí)建議一個(gè)完整的語(yǔ)句放在一行,不要根據(jù)字符數(shù)斷行
規(guī)則9.4 多個(gè)短語(yǔ)句(包括賦值語(yǔ)句)不允許寫在同一行內(nèi),即一行只寫一條語(yǔ)句。
規(guī)則9.5 if、for、do、while、case、switch、default等語(yǔ)句獨(dú)占一行。
規(guī)則9.6 在兩個(gè)以上的關(guān)鍵字、變量、常量進(jìn)行對(duì)等操作時(shí),它們之間的操作符之前、之后或者前后要加空格;進(jìn)行非對(duì)等操作時(shí),如果是關(guān)系密切的立即操作符(如->),后不應(yīng)加空格。
建議9.1 注釋符(包括?/??//??/?)與注釋內(nèi)容之間要用一個(gè)空格進(jìn)行分隔。
建議9.2 源程序中關(guān)系較為緊密的代碼應(yīng)盡可能相鄰。
十、 表達(dá)式
規(guī)則10.1 表達(dá)式的值在標(biāo)準(zhǔn)所允許的任何運(yùn)算次序下都應(yīng)該是相同的。
建議10.1 函數(shù)調(diào)用不要作為另一個(gè)函數(shù)的參數(shù)使用,否則對(duì)于代碼的調(diào)試、閱讀都不利。
建議10.2 賦值語(yǔ)句不要寫在if等語(yǔ)句中,或者作為函數(shù)的參數(shù)使用。
建議10.3 賦值操作符不能使用在產(chǎn)生布爾值的表達(dá)式上。
十一、 代碼編輯、編譯
規(guī)則11.1 使用編譯器的最高告警級(jí)別,理解所有的告警,通過修改代碼而不是降低告警級(jí)別來消除所有告警。
規(guī)則11.2 在產(chǎn)品軟件(項(xiàng)目組)中,要統(tǒng)一編譯開關(guān)、靜態(tài)檢查選項(xiàng)以及相應(yīng)告警清除策略。
規(guī)則11.3 本地構(gòu)建工具(如PC-Lint)的配置應(yīng)該和持續(xù)集成的一致。
規(guī)則11.4 使用版本控制(配置管理)系統(tǒng),及時(shí)簽入通過本地構(gòu)建的代碼,確保簽入的代碼不會(huì)影響構(gòu)建成功。
建議11.1 要小心地使用編輯器提供的塊拷貝功能編程。
十二、 可測(cè)性
原則12.1 模塊劃分清晰,接口明確,耦合性小,有明確輸入和輸出,否則單元測(cè)試實(shí)施困難。
說明:單元測(cè)試實(shí)施依賴于:
模塊間的接口定義清楚、完整、穩(wěn)定;
模塊功能的有明確的驗(yàn)收條件(包括:預(yù)置條件、輸入和預(yù)期結(jié)果);
模塊內(nèi)部的關(guān)鍵狀態(tài)和關(guān)鍵數(shù)據(jù)可以查詢,可以修改;
模塊原子功能的入口唯一;
模塊原子功能的出口唯一;
依賴集中處理:和模塊相關(guān)的全局變量盡量的少,或者采用某種封裝形式。
規(guī)則12.1 在同一項(xiàng)目組或產(chǎn)品組內(nèi),要有一套統(tǒng)一的為集成測(cè)試與系統(tǒng)聯(lián)調(diào)準(zhǔn)備的調(diào)測(cè)開關(guān)及相應(yīng)打印函數(shù),并且要有詳細(xì)的說明。
規(guī)則12.2 在同一項(xiàng)目組或產(chǎn)品組內(nèi),調(diào)測(cè)打印的日志要有統(tǒng)一的規(guī)定。
說明:統(tǒng)一的調(diào)測(cè)日志記錄便于集成測(cè)試,具體包括:
統(tǒng)一的日志分類以及日志級(jí)別;
通過命令行、網(wǎng)管等方式可以配置和改變?nèi)罩据敵龅膬?nèi)容和格式;
在關(guān)鍵分支要記錄日志,日志建議不要記錄在原子函數(shù)中,否則難以定位;
調(diào)試日志記錄的內(nèi)容需要包括文件名/模塊名、代碼行號(hào)、函數(shù)名、被調(diào)用函數(shù)名、錯(cuò)誤碼、錯(cuò)誤發(fā)生的環(huán)境等。
規(guī)則12.3 使用斷言記錄內(nèi)部假設(shè)。
規(guī)則12.4 不能用斷言來檢查運(yùn)行時(shí)錯(cuò)誤。
說明:斷言是用來處理內(nèi)部編程或設(shè)計(jì)是否符合假設(shè);不能處理對(duì)于可能會(huì)發(fā)生的且必須處理的情況要寫防錯(cuò)程序,而不是斷言。如某模塊收到其它模塊或鏈路上的消息后,要對(duì)消息的合理性進(jìn)行檢查,此過程為正常的錯(cuò)誤檢查,不能用斷言來實(shí)現(xiàn)。
建議12.1 為單元測(cè)試和系統(tǒng)故障注入測(cè)試準(zhǔn)備好方法和通道。
十三、 安全性
原則13.1 對(duì)用戶輸入進(jìn)行檢查。
說明:不能假定用戶輸入都是合法的,因?yàn)殡y以保證不存在惡意用戶,即使是合法用戶也可能由于誤用誤操作而產(chǎn)生非法輸入。用戶輸入通常需要經(jīng)過檢驗(yàn)以保證安全,特別是以下場(chǎng)景:
用戶輸入作為循環(huán)條件
用戶輸入作為數(shù)組下標(biāo)
用戶輸入作為內(nèi)存分配的尺寸參數(shù)
用戶輸入作為格式化字符串
用戶輸入作為業(yè)務(wù)數(shù)據(jù)(如作為命令執(zhí)行參數(shù)、拼裝sql語(yǔ)句、以特定格式持久化)
這些情況下如果不對(duì)用戶數(shù)據(jù)做合法性驗(yàn)證,很可能導(dǎo)致DOS、內(nèi)存越界、格式化字符串漏洞、命令注入、SQL注入、緩沖區(qū)溢出、數(shù)據(jù)破壞等問題。
可采取以下措施對(duì)用戶輸入檢查:
用戶輸入作為數(shù)值的,做數(shù)值范圍檢查
用戶輸入是字符串的,檢查字符串長(zhǎng)度
用戶輸入作為格式化字符串的,檢查關(guān)鍵字“%”
用戶輸入作為業(yè)務(wù)數(shù)據(jù),對(duì)關(guān)鍵字進(jìn)行檢查、轉(zhuǎn)義
規(guī)則13.1 確保所有字符串是以NULL結(jié)束。
說明: C語(yǔ)言中??作為字符串的結(jié)束符,即NULL結(jié)束符。標(biāo)準(zhǔn)字符串處理函數(shù)(如strcpy()、 strlen())
依賴NULL結(jié)束符來確定字符串的長(zhǎng)度。沒有正確使用NULL結(jié)束字符串會(huì)導(dǎo)致緩沖區(qū)溢出和其它未定義的行為。
為了避免緩沖區(qū)溢出,常常會(huì)用相對(duì)安全的限制字符數(shù)量的字符串操作函數(shù)代替一些危險(xiǎn)函數(shù)。如:
用strncpy()代替strcpy()
用strncat()代替strcat()
用snprintf()代替sprintf()
用fgets()代替gets()
這些函數(shù)會(huì)截?cái)喑鲋付ㄏ拗频淖址?但是要注意它們并不能保證目標(biāo)字符串總是以NULL結(jié)尾。如果源字符串的前n個(gè)字符中不存在NULL字符,目標(biāo)字符串就不是以NULL結(jié)尾。
規(guī)則13.2 不要將邊界不明確的字符串寫到固定長(zhǎng)度的數(shù)組中。
說明:邊界不明確的字符串(如來自gets()、getenv()、scanf()的字符串),長(zhǎng)度可能大于目標(biāo)數(shù)組長(zhǎng)度,直接拷貝到固定長(zhǎng)度的數(shù)組中容易導(dǎo)致緩沖區(qū)溢出。
規(guī)則13.3 避免整數(shù)溢出。
說明:當(dāng)一個(gè)整數(shù)被增加超過其最大值時(shí)會(huì)發(fā)生整數(shù)上溢,被減小小于其最小值時(shí)會(huì)發(fā)生整數(shù)下溢。帶符號(hào)和無符號(hào)的數(shù)都有可能發(fā)生溢出。
規(guī)則13.4 避免符號(hào)錯(cuò)誤。
說明:有時(shí)從帶符號(hào)整型轉(zhuǎn)換到無符號(hào)整型會(huì)發(fā)生符號(hào)錯(cuò)誤,符號(hào)錯(cuò)誤并不丟失數(shù)據(jù),但數(shù)據(jù)失去了原來的含義。
帶符號(hào)整型轉(zhuǎn)換到無符號(hào)整型,最高位(high-order bit)會(huì)喪失其作為符號(hào)位的功能。如果該帶符號(hào)整數(shù)的值非負(fù),那么轉(zhuǎn)換后值不變;如果該帶符號(hào)整數(shù)的值為負(fù),那么轉(zhuǎn)換后的結(jié)果通常是一個(gè)非常大的正數(shù)。
規(guī)則13.5:避免截?cái)噱e(cuò)誤。
說明:將一個(gè)較大整型轉(zhuǎn)換為較小整型,并且該數(shù)的原值超出較小類型的表示范圍,就會(huì)發(fā)生截?cái)噱e(cuò)誤,原值的低位被保留而高位被丟棄。截?cái)噱e(cuò)誤會(huì)引起數(shù)據(jù)丟失。使用截?cái)嗪蟮淖兞窟M(jìn)行內(nèi)存操作,很可能會(huì)引發(fā)問題。
規(guī)則13.6:確保格式字符和參數(shù)匹配。
說明:使用格式化字符串應(yīng)該小心,確保格式字符和參數(shù)之間的匹配,保留數(shù)量和數(shù)據(jù)類型。格式字符和參數(shù)之間的不匹配會(huì)導(dǎo)致未定義的行為。大多數(shù)情況下,不正確的格式化字符串會(huì)導(dǎo)致程序異常終止。
規(guī)則13.7 避免將用戶輸入作為格式化字符串的一部分或者全部。
說明:調(diào)用格式化I/O函數(shù)時(shí),不要直接或者間接將用戶輸入作為格式化字符串的一部分或者全部。攻擊者對(duì)一個(gè)格式化字符串擁有部分或完全控制,存在以下風(fēng)險(xiǎn):進(jìn)程崩潰、查看棧的內(nèi)容、改寫內(nèi)存、甚至執(zhí)行任意代碼。
規(guī)則13.8 避免使用strlen()計(jì)算二進(jìn)制數(shù)據(jù)的長(zhǎng)度。
說明:strlen()函數(shù)用于計(jì)算字符串的長(zhǎng)度,它返回字符串中第一個(gè)NULL結(jié)束符之前的字符的數(shù)量。因此用strlen()處理文件I/O函數(shù)讀取的內(nèi)容時(shí)要小心,因?yàn)檫@些內(nèi)容可能是二進(jìn)制也可能是文本。
規(guī)則13.9 使用int類型變量來接受字符I/O函數(shù)的返回值。
規(guī)則13.10 防止命令注入。
說明:C99函數(shù)system()通過調(diào)用一個(gè)系統(tǒng)定義的命令解析器(如UNIX的shell,Windows的CMD.exe)來執(zhí)行一個(gè)指定的程序/命令。類似的還有POSIX的函數(shù)popen()。
十四、 單元測(cè)試
規(guī)則14.1 在編寫代碼的同時(shí),或者編寫代碼前,編寫單元測(cè)試用例驗(yàn)證軟件設(shè)計(jì)/編碼的正確。
建議14.1 單元測(cè)試關(guān)注單元的行為而不是實(shí)現(xiàn),避免針對(duì)函數(shù)的測(cè)試。
說明:應(yīng)該將被測(cè)單元看做一個(gè)被測(cè)的整體,根據(jù)實(shí)際資源、進(jìn)度和質(zhì)量風(fēng)險(xiǎn),權(quán)衡代碼覆蓋、打樁工作量、補(bǔ)充測(cè)試用例的難度、被測(cè)對(duì)象的穩(wěn)定程度等,一般情況下建議關(guān)注模塊/組件的測(cè)試,盡量避免針對(duì)函數(shù)的測(cè)試。盡管有時(shí)候單個(gè)用例只能專注于對(duì)某個(gè)具體函數(shù)的測(cè)試,但我們關(guān)注的應(yīng)該是函數(shù)的行為而不是其具體實(shí)現(xiàn)細(xì)節(jié)。
十五、 可移植性
規(guī)則15.1 不能定義、重定義或取消定義標(biāo)準(zhǔn)庫(kù)/平臺(tái)中保留的標(biāo)識(shí)符、宏和函數(shù)。
建議15.1 不使用與硬件或操作系統(tǒng)關(guān)系很大的語(yǔ)句,而使用建議的標(biāo)準(zhǔn)語(yǔ)句,以提高軟件的可移植性和可重用性。
說明:程序中嵌入式匯編,一般都對(duì)可移植性有較大的影響。
編輯:黃飛
評(píng)論
查看更多