編碼規范,沒有最好,只有最合適,有但不執行不如沒有。
一、編碼原則
01
可讀性
清晰第一
清晰性是易于維護程序必須具備的特征。維護期變更代碼的成本遠遠大于開發期,編寫程序應該以人為本,計算機第二。一般情況下,代碼的可閱讀性高于性能,只有定性能是瓶頸時,才應該主動優化。
簡潔為美
簡潔就是易于理解并且易于實現。代碼越長越難以看懂,也就越容易在修改時引入錯誤。提倡通過簡潔明了的代碼來提升代碼可靠性。廢棄的代碼要及時清除,重復代碼應該盡可能提煉成函數。
風格一致
所有人共同分享同一種風格,為后期維護,和代碼交接帶來便捷。
02
設計原則
- 開放封閉原則對于擴展是開放的,對于修改是封閉的。
- 單一職責原則每一個子函數或者類似的代碼塊應該只有一個職責,所以只有一個原因會使其改變。
- 接口隔離原則接口盡量細化,同時接口中的方法盡量少。
- 最少知道原則一個子模塊應該與其它模塊保持最少的了解。
- 依賴倒置原則高層模塊,低層模塊,細節(實現)都應該依賴抽象(即接口)。
二、編碼規范
01
文件頭申明
新增.c必須添加注釋,標注公司名稱、文件功能說明,創建日期、作者,后續修改說明范例如下:
可配置Source Insight 自動生成模板。
02
文件
所有.h頭文件必須采取阻止內容被包含多于一次的機制。
- 頭文件對外接口,應放置對外部的聲明,如對外提供的函數聲明、宏定義、類型定義等。
內部使用的函數聲明不應放在頭文件中。
內部使用的宏、枚舉、結構定義不應放入頭文件中。
變量定義禁止在頭文件中,應放在.c文件中。
模塊內使用的全局變量,不應通過在頭文件中聲明的方式直接暴露給外部。
頭文件中只包含接口的聲明,不含實現。
頭文件應當職責單一,頭文件過于復雜,依賴過于復雜是導致編譯時間過長的主要原因。
每一個.c文件應有一個同名.h文件,用于聲明需要對外公開的接口。
禁止頭文件循環依賴,禁止包含用不到的頭文件。
每個.c源文件內容片段按如下順序,文件注釋-包含頭文件-宏定義-數據結構定義-變量定義-引用外部變量-引用外部函數-本地函數-全局函數。
03
函數
- 一個函數僅完成一件功能。
- 重復代碼應該盡可能提煉成函數。說明:重復代碼提煉成函數可以帶來維護成本的降低。重復代碼是不良代碼最典型的特征之一。在“代碼能用就不改”的指導原則之下,新需求增加帶來的代碼拷貝和修改,隨著時間的遷移,產品中堆砌著許多類似或者重復的代碼。
避免遞歸函數的代碼塊嵌套過深。
對函數的錯誤返回碼要全面處理。說明:一個函數(標準庫中的函數/第三方庫函數/用戶定義的函數)能夠提供一些指示錯誤發生的方法,可以通過使用錯誤標記、特殊的返回數據或者其他手段,調用程序應該在函數返回時立刻檢查錯誤指示。
廢棄函數要及時清除。說明:程序中的廢棄代碼不僅占用額外的空間,而且還常常影響程序的功能與性能,很可能給程序的測試、維護等造成不必要的麻煩。
函數傳入的不變參數使用const限制。
函數的參數個數不超過5個,檢查輸入參數的有效性。說明:函數的參數過多,會使得該函數易于受外部(其他部分的代碼)變化的影響,從而影響維護工作。函數的參數過多同時也會增大測試的工作量。函數的參數個數不要超過5個,如果超過了建議拆分為不同函數;函數的輸入主要有兩種:一種是參數輸入;另一種是全局變量、數據文件的輸入,即非參數輸入。函數在使用輸入參數之前,應進行有效性檢查。
源文件范圍內聲明和定義的所有函數,除非外部可見,否則增加static關鍵字,針對單元測試的特殊情況,對這類函數盡量封裝一層再使用。
傳入參數表意有3種以上的禁止使用魔法數,必須使用枚舉值且附帶注釋。
函數內部要對參數的合法性進行檢查。說明:函數的輸入主要有兩種:一種是參數輸入;另一種是全局變量、數據文件的輸入,即非參數輸入。函數在使用輸入參數之前,應進行有效性檢查。
除打印類函數外,不要使用可變長函數。說明:可變長參函數的處理過程比較復雜容易引入錯誤,而且性能也比較低,使用過多的可變長參函數將導致函數的維護難度大大增加。
每個函數都要返回錯誤碼,調用程序必須在函數返回時檢查錯誤碼。
標識符的命名要清晰明了,有明確含義,使用完整的單詞,盡量避免名字中出現數字編號或特殊符號。
函數名稱需體現出函數具體功能,均由功能單詞拼接組成,絕不允許出現中文拼音。
函數命名應以函數要執行的動作命名,一般采用動詞或者動詞+名詞的結構。
04
變量
- 不用或者少用全局變量。說明:單個文件內部可以使用static的全局變量,可以將其理解為類的私有成員變量。全局變量應該是模塊的私有數據,不能作用對外的接口使用,使用static類型定義,可以有效防止外部文件的非正常訪問。直接使用其他模塊的私有數據,將使模塊間的關系逐漸走向“剪不斷理還亂”的耦合狀態,這種情形是不允許的。
避免局部變量與全局變量同名。說明:盡管局部變量和全局變量的作用域不同而不會發生語法錯誤,但容易使人誤解。
嚴禁使用未經初始化的變量。
明確全局變量的初始化順序,避免跨模塊的初始化依賴。說明:系統啟動階段,使用全局變量前,要考慮到該全局變量在什么時候初始化,兩者之間的時序關系,誰先誰后,一定要分析清楚,不然后果往往是低級而又災難性的。
數據必須對外開放時,應封裝接口函數來讀寫,同時注意全局數據的訪問互斥。說明:避免直接暴露內部數據給外部模型使用,是防止模塊間耦合最簡單有效的方法。
一個變量只有一個功能,不能把一個變量用作多種用途。說明:一個變量只用來表示一個特定功能,不能把一個變量作多種用途,即同一變量取值不同時,其代表的意義也不同。
數據結構功能單一,不要設計面面俱到的數據結構。說明:相關的一組信息才是構成一個結構體的基礎,結構的定義應該可以明確的描述一個對象,而不是一組相關性不強的數據的集合。設計結構時應力爭使結構代表一種現實事務的抽象,而不是同時代表多種。結構中的各元素應代表同一事務的不同側面,而不應把描述沒有關系或關系很弱的不同事務的元素放到同一結構體中。
盡量減少沒有必要的數據類型默認轉換與強制轉換。說明:當進行數據類型強制轉換時,其數據的意義、轉換后的取值等都有可能發生變化,而這些細節若考慮不周,就很有可能留下隱患。
示例:如下賦值,多數編譯器不產生告警,但值的含義有變化。
確認未使用的變量應當刪除。對于變量自增 ++ 和自減--,禁止在宏定義中使用,禁止和其他語句復合,因拆分單獨執行。示例:if(++i>10) 錯誤寫法,必須改為i++;if(i>10)
05
宏和常量
宏定義和常量使用大寫字母或下劃線。
用宏定義表達式時,要使用完備的括號,如下:
宏定義中盡量不要使用return、goto、continue、break等改變程序流程的語句。
常量建議使用const定義代替宏,如下:
除非必要,應盡可能使用函數代替宏 。
將宏定義的多條表達式放在大括號中。
使用宏時,不允許參數發生變化。
盡量少用魔法數,或者必須加注釋說明,或者修改方案,如內存長度操作禁止使用常數,非特殊情況必須使用sizeof自動處理。
06
命名
命名采用unix like風格,單詞用小寫字母,每個單詞之間用下劃線分割,引用的第三方的代碼可保持原有風格,命名盡量使用通用英文單詞或縮寫。
文件:
文件名命名可根據平臺自有規則命名,一般采用小寫字符,字段之間使用下劃線分隔;相同功能的 .c和.h文件名相同。
枚舉:
枚舉定義:宏定義和枚舉值禁止使用小寫字母,不能以下劃線開頭,字段之間使用下劃線分隔,若邏輯中要標注多種狀態,狀態不允許用數字表示。
結構體:
結構體定義,若同一功能所使用到的參數,盡量用結構體來定義表示,便于相關參數獲取和設置。
純業務邏輯代碼,與平臺無關的,必須使用小寫字符和下劃線分隔。
函數函數名定義:
函數名稱需體現出函數具體功能,均由功能單詞拼接組成,使用小寫字母和下劃線拼接,其中全局函數必須以xx_為前綴,在.h里面申明全局函數,補充完整注釋;局部函數使用static限制。
變量:
禁止使用全大寫字母命名變量,全局變量至少5個字母,使用高頻次的全局變量盡量簡短。
全局變量命名表達其作用,且以小寫字母g_開頭,后面拼接功能英文,如地址:g_addr。
變量名的拼接,全部使用小寫字母和下劃線拼接,函數內局部變量允許使用單個字母。
多個同類的變量封裝成結構體。
推薦命名:
07
注釋
注釋應放在其代碼上方相鄰位置或右方,不可放在下面。
注釋的內容要清楚明了,防止注釋二義性。
修改代碼時同步更新注釋,保證注釋與代碼的一致性。
函數聲明處注釋描述函數功能、性能及用法,提供參考范本如下:
全局變量要有較詳細的注釋:
函數內部不是注釋越多越好,而是變量命名和邏輯清晰,自注釋最好,特殊情況或者需要特別注意的地方才加注釋,并且注釋要放在代碼行的上方。
基于SDK開發,在基線工程上改動代碼,不允許刪除源代碼,修改代碼必須增加注釋,必須使用關鍵字“XX_CODE”標注修改原因,方便后續打補丁,范例如下:
對于非c源碼的文件,在這個注釋格式的基礎上,每行添加對應的注釋符號。
修改與外設驅動、通信協議、系統底層等相關的代碼,具有特殊隱含限制的代碼,必須提交詳細的修改原因,便于后續版本回溯查找原因。
復雜且相對獨立的功能,單獨使用markdown文檔說明開發方案、實現技術、應用場景、使用限制等,隨代碼提交。
08
排版與格式
程序塊采用縮進風格編寫,每級縮進為4個空格。
相對獨立的程序塊之間、變量說明之后必須加空行。
多個短語句不允許寫在同一行內,長語句不能拆分需要分行寫。
if、for、do、while、case、switch、default等語句獨占一行,{換行且獨占一行。
賦值語句不要寫在if等語句中,或者作為函數的參數使用。
邏輯表達式每個子項都使用()。
if與else if/else必須以’{}’分隔,且 ‘{’與‘}’各占一行,if-else分3層以上必須以else子句結束,即使操作為空,并增加注釋://do nothing
switch語句必須有default分支。
在兩個以上的關鍵字、變量、常量進行對等操作時,它們之間的操作符之前、之后或者前后要加空格;進行非對等操作時(如->),后面不應加空格。
文件編寫完成后,統一使用Astyle自動格式化工具整理一遍再提交到版本庫。
三、編碼要求
01
安全性
對用戶輸入數值進行有效范圍檢查。
對輸入參數進行邊界判斷,尤其對指針變量。
嚴禁使用未經初始化的變量作為右值,所有變量都要初始化。
每個普通變量(整型、字符型)的定義都要考慮范圍,嚴防溢出。
每個數組的定義和使用都要嚴防越界的發生。
要盡量避免隱式或者顯式的類型轉換,防止截斷的發生或者符號的丟失。
動態申請的內存盡量在本函數內釋放,特殊情況下,必須補充注釋提醒外界釋放內存。
02
可移植性
不能定義、重定義或取消定義標準庫/平臺中保留的標識符、宏和函數。
提取與平臺有關的通用函數,封裝后統一放在固定文件,方便后期替換升級。
源文件必須使用原有平臺、SDK一致的文件編碼(GB2312/UTF8),有效代碼中不得出現中文字符。
通用功能使用功能宏控制,且代碼集中。
使用系統接口,尤其是不同平臺API不同的,使用函數名中帶pal的接口封裝并注釋,便于后續移植到其他平臺。
引用第三方開源代碼,允許保留其原始風格,客制化修改必須補充完整注釋。
四、規范實施
編碼規范是軟件開發團隊合作的標準,但實際開發過程中存在各種不可控因素,尤其是項目進度壓力和開發者水平與認知的差異,導致軟件規則不能嚴格執行。隨著軟件工程規模的擴大,軟件交期、代碼同步、重構或交接,其風險也逐漸放大。因此,存在合適的編碼規則并不能解決問題,只有強制代碼格式化,才能真正落實編碼規范統一。
軟件質量是項目成敗的關鍵點之一,在開發周期有限,人力資源不足的情況下,使用工具實現代碼自動掃描,分析出潛在隱患點,從源頭減少軟件bug,是軟件如期交付的重要保證。實現代碼自動格式化和靜態分析,可以有效規避軟件風險。
-
C語言
+關注
關注
180文章
7614瀏覽量
137436 -
編碼
+關注
關注
6文章
957瀏覽量
54912 -
函數
+關注
關注
3文章
4345瀏覽量
62882
發布評論請先 登錄
相關推薦
評論