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