最全面的C/C++編碼規範總結
對於不同的程式語言來說,具體的編碼規範可以有很大的不同,但是其宗旨都是一致的,就是保證程式碼在高質量完成需求的同時具備良好的可讀性、可維護性。例如我們可以規定某個專案的C語言程式要遵循這樣的規定:變數的命名,標頭檔案的書寫和#include
等等。
下面是一些廣為採用的編碼規範:
- GNU Coding Standards
- Guidelines for the Use of the C Language in Vehicle Based Software
- C++ Coding Guidelines
- SUN Code Conventions for Java
以下是一些介紹編碼、編碼規範的書籍:
- C++編碼規範,陳世忠,人民郵電出版社,2002
- 高質量程式設計指南:C++/C語言,林銳等,電子工業出版社,2003
注:以下只是根據課題組已有的經驗給出的總結,並非對所有場景均適用。
對於高質量的工程,一般會做到:
- 程式碼簡潔精煉,美觀,可讀性好,高效率,高複用,可移植性好,高內聚,低耦合,沒有冗餘,不符合這些原則,必須特別說明。
- 規範性,程式碼有規可循。特殊排版、特殊語法、特殊指令,必須特別說明。
一、檔案排版方面
- 包含標頭檔案
1.1 先系統標頭檔案,後使用者標頭檔案。
1.2 系統標頭檔案,穩定的目錄結構,應採用包含子路徑方式。
1.3 自定義標頭檔案,不穩定目錄結構,應在dsp中指定包含路徑。
1.4 系統標頭檔案應用:#include <xxx.h>
1.5 自定義同檔案應用:#include "xxx.h"
1.6 只引用需要的標頭檔案。 - h和cpp檔案
2.1 標頭檔案命名為*.h
,內聯檔案命名為*.inl
;C++檔案命名為*.cpp
2.2 檔名用大小寫混合,或者小寫混合。例如DiyMainView.cpp
,infoview.cpp
。不要用無意義的名稱:例如XImage.cpp
;SView.cpp
;xlog.cpp
;
2.3 標頭檔案除了特殊情況,應使用#ifdef
控制塊。
2.4 標頭檔案#endif
應採用行尾註釋。
2.5 標頭檔案,首先是包含程式碼塊,其次是巨集定義程式碼塊,然後是全域性變數,全域性常量,型別定義,類定義,內聯部分。
2.6 CPP檔案,包含指令,巨集定義,全域性變數,函式定義。 - 檔案結構
3.1 檔案應包含檔案頭註釋和內容。
3.2 函式體類體之間原則上用2個空行,特殊情況下可用一個或者不需要空行。 - 空行
4.1 檔案頭、控制塊,#include
部分、巨集定義部分、class
部分、全域性常量部分、全域性變數部分、函式和函式之間,用兩個空行。
二、註釋方面
- 檔案頭註釋
1.1 作者,檔名稱,檔案說明,生成日期(可選) - 函式註釋
2.1 關鍵函式必須寫上註釋,說明函式的用途。
2.2 特別函式引數,需要說明引數的目的,由誰負責釋放等等。
2.3 除了特別情況,註釋寫在程式碼之前,不要放到程式碼行之後。
2.4 對每個#else
或#endif
給出行末註釋。
2.5 關鍵程式碼註釋,包括但不限於:賦值,函式呼叫,表示式,分支等等。
2.6 善未實現完整的程式碼,或者需要進一步優化的程式碼,應加上// TODO ...
2.7 除錯的程式碼,加上註釋// only for DEBUG
2.8 需要引起關注的程式碼,加上註釋// NOTE ...
2.9 對於較大的程式碼塊結尾,如for,while,do
等,可加上// end for|while|do
三、命名方面
- 原則
1.1 同一性:在編寫一個子模組或派生類的時候,要遵循其基類或整體模組的命名風格,保持命名風格在整個模組中的同一性。
1.2 識別符號組成:識別符號采用英文單詞或其組合,應當直觀且可以拼讀,可望文知意,用詞應當準確,避免用拼音命名。
1.3 最小化長度 && 最大化資訊量原則:在保持一個識別符號意思明確的同時,應當儘量縮短其長度。
1.4 避免過於相似:不要出現僅靠大小寫區分的相似的識別符號,例如"i"
與"I"
,"function"
與"Function"
等等。
1.5 避免在不同級別的作用域中重名:程式中不要出現名字完全相同的區域性變數和全域性變數,儘管兩者的作用域不同而不會發生語法錯誤,但容易使人誤解。
1.6 正確命名具有互斥意義的識別符號:用正確的反義片語命名具有互斥意義的識別符號,如:"nMinValue"
和"nMaxValue"
,"GetName()"
和"SetName()"
….
1.7 避免名字中出現數字編號:儘量避免名字中出現數字編號,如Value1,Value2
等,除非邏輯上的確需要編號。這是為了防止程式設計師偷懶,不肯為命名動腦筋而導致產生無意義的名字(因為用數字編號最省事)。 - T,C,M,R類
2.1 T類表示簡單資料型別,不對資源擁有控制權,在析構過程中沒有釋放資源動作。
2.2 C表示從CBase繼承的類。該類不能從棧上定義變數,只能從堆上建立。
2.3 M表示介面類。
2.4 R是資源類,通常是系統固有型別。除了特殊情況,不應在開發程式碼中出現R型別。 - 函式名
3.1 M類的函式名稱應採用HandleXXX
命名,例如:HandleTimerEvent
;不推薦採用java風格,例如handleTimerEvent
;除了標準c風格程式碼,不推薦用下劃線,例如,handle_event
。
3.2 Leave函式,用字尾L。
3.3 Leave函式,且進清除棧,用字尾LC。
3.4 Leave函式,且刪除物件,用字尾LD。 - 函式引數
4.1 函式引數用a作為字首。
4.2 避免出現和匈牙利混合的命名規則如apBuffer
名稱。用aBuffer
即可。
4.3 函式引數比較多時,應考慮用結構代替。
4.4 如果不能避免函式引數比較多,應在排版上可考慮每個引數佔用一行,引數名豎向對齊。 - 成員變數
5.1 成員變數用m最為字首。
5.2 避免出現和匈牙利混合的命名規則如mpBuffer
名稱。用mBuffer
即可。 - 區域性變數
6.1 迴圈變數和簡單變數採用簡單小寫字串即可。例如,int i
;
6.2 指標變數用p
打頭,例如void* pBuffer;
- 全域性變數
7.1 全域性變數用g_
最為字首。 - 類名
8.1 類和物件名應是名詞。
8.2 實現行為的類成員函式名應是動詞。
8.3 類的存取和查詢成員函式名應是名詞或形容詞。 - 風格相容性
9.1 對於移植的或者開源的程式碼,可以沿用原有風格,不用C++的命名規範。
四、程式碼風格方面
- Tab和空格
1.1 每一行開始處的縮排只能用Tab,不能用空格,輸入內容之後統一用空格。除了最開始的縮排控制用Tab,其他部分為了對齊,需要使用空格進行縮排。這樣可以避免在不同的編輯器下顯示不對齊的情況。
1.2 在程式碼行的結尾部分不能出現多餘的空格。
1.3 不要在"::","->","."
前後加空格。
1.4 不要在",",";"
之前加空格。 - 型別定義和{
2.1 類,結構,列舉,聯合:大括號另起一行 - 函式
3.1 函式體的{需要新起一行,在{之前不能有縮排。
3.2 除了特別情況,函式體內不能出現兩個空行。
3.3 除了特別情況,函式體內不能巨集定義指令。
3.4 在一個函式體內,邏揖上密切相關的語句之間不加空行,其它地方應加空行分隔。
3.5 在標頭檔案定義的inline
函式,函式之間可以不用空行,推薦用一個空行。 - 程式碼塊
4.1"if"、"for"、"while"、"do"、"try"、"catch"
等語句自佔一行,執行語句不得緊跟其後。不論執行語句有多少都要加"{ }"
。這樣可以防止書寫和修改程式碼時出現失誤。
4.2"if"、"for"、"while"、"do"、"try"、"catch"
的括號和表示式,括號可緊挨關鍵字,這樣強調的是表示式。 - else
5.1 if語句如果有else語句,用 } else { 編寫為一行,不推薦用 3 行程式碼的方式。 - 程式碼行
6.1 一行程式碼只做一件事情,如只定義一個變數,或只寫一條語句。這樣的程式碼容易閱讀,並且方便於寫註釋。
6.2 多行變數定義,為了追求程式碼排版美觀,可將變數豎向對齊。
6.3 程式碼行最大長度宜控制在一定個字元以內,能在當前螢幕內全部可見為宜。 - switch語句
7.1 case關鍵字應和switch對齊。
7.2 case子語句如果有變數,應用{}包含起來。
7.3 如果有並列的類似的簡單case語句,可考慮將case程式碼塊寫為一行程式碼。
7.4 簡單的case之間可不用空行,複雜的case之間應考慮用空行分割開。
7.5 case字語句的大括號另起一行,不要和case寫到一行。
7.6 為所有switch語句提供default分支。
7.7 若某個case不需要break一定要加註釋宣告。 - 迴圈
8.1 空迴圈可用for( ;; )
或者while( 1 )
或者while( true )
- 類
9.1 類繼承應採用每個基類佔據一行的方式。
9.2 單繼承可將基類放在類定義的同一行。如果用多行,則應用Tab縮排。
9.3 多繼承在基類比較多的情況下,應將基類分行,並採用Tab縮排對齊。
9.4 過載基類虛擬函式,應在該組虛擬函式前寫註釋// implement XXX
9.5 友元宣告放到類的末尾。 - 巨集
10.1 不要用分號結束巨集定義。
10.2 函式巨集的每個引數都要括起來。
10.3 不帶引數的巨集函式也要定義成函式形式。 - goto
11.1 儘量不要用goto。
五、型別
- 定義指標和引用時
*
和&
緊跟型別。 - 儘量避免使用浮點數,除非必須。
- 用
typedef
簡化程式中的複雜語法。 - 避免定義無名稱的型別。例如:
typedef enum { EIdle, EActive } TState;
- 少用
union
,如果一定要用,則採用簡單資料型別成員。 - 用
enum
取代(一組相關的)常量。 - 不要使用魔鬼數字。
- 儘量用引用取代指標。
- 定義變數完成後立即初始化,勿等到使用時才進行。
- 如果有更優雅的解決方案,不要使用強制型別轉換。
六、表示式
- 避免在表示式中用賦值語句。
- 避免對浮點型別做等於或不等於判斷。
- 不能將列舉型別進行運算後再賦給列舉變數。
- 在迴圈過程中不要修改迴圈計數器。
- 檢測空指標,用
if( p )
- 檢測非空指標,用
if( ! p )
七、函式
- 引用
1.1 引用型別作為返回值:函式必須返回一個存在的物件。
1.2 引用型別作為引數:呼叫者必須傳遞一個存在的物件。 - 常量成員函式
2.1 表示該函式只讀取物件的內容,不會對物件進行修改。 - 返回值
3.1 除開void
函式,建構函式,解構函式,其它函式必須要有返回值。
3.2 當函式返回引用或指標時,用文字描述其有效期。 - 行內函數
4.1 行內函數應將函式體放到類體外。
4.2 只有簡單的函式才有必要設計為行內函數,複雜業務邏輯的函式不要這麼做。
4.3 虛擬函式不要設計為行內函數。 - 函式引數
5.1 只讀取該引數的內容,不對其內容做修改,用常量引用。
5.2 修改引數內容,或需要通過引數返回,用非常量應用。
5.3 簡單資料型別用傳值方式。
5.4 複雜資料型別用引用或指標方式。
八、類
- 建構函式
1.1 建構函式的初始化列表,應和類的順序一致。
1.2 初始化列表中的每個項,應獨佔一行。
1.3 避免出現用一個成員初始化另一個成員。
1.4 建構函式應初始化所有成員,尤其是指標。
1.5 不要在建構函式和解構函式中丟擲異常。 - 純虛擬函式
2.1 M類的虛擬函式應設計為純虛擬函式。 - 構造和解構函式
3.1 如果類可以繼承,則應將類解構函式設計為虛擬函式。
3.2 如果類不允許繼承,則應將類解構函式設計為非虛擬函式。
3.3 如果類不能被複制,則應將拷貝建構函式和賦值運算子設計為私有的。
3.4 如果為類設計了建構函式,則應有解構函式。 - 成員變數
4.1 儘量避免使用mutable
和Volatile
。
4.2 儘量避免使用公有成員變數。 - 成員函式
5.1 努力使類的介面少而完備。
5.2 儘量使用常成員函式代替非常成員函式,const
函式
5.3 除非特別理由,絕不要重新定義(繼承來的)非虛擬函式。(這樣是覆蓋,基類的某些屬性無初始化) - 繼承
6.1 繼承必須滿足IS-A的關係,HAS-A應採用包含。
6.2 虛擬函式不要採用預設引數。
6.3 除非特別需要,應避免設計大而全的虛擬函式,虛擬函式功能要單一。
6.4 除非特別需要,避免將基類強制轉換成派生類。 - 友元
7.1 儘量避免使用友元函式和友元類。
九、錯誤處理
- 申請記憶體用
new
操作符。 - 釋放記憶體用
delete
操作符。 new
和delete
,new[]
和delete[]
成對使用。- 申請記憶體完成之後,要檢測指標是否申請成功,處理申請失敗的情況。
- 誰申請誰釋放。優先順序:函式層面,類層面,模組層面。
- 釋放記憶體完成後將指標賦空,避免出現野指標。
- 使用指標前進行判斷合法性,應考慮到為空的情況的處理。
- 使用陣列時,應先判斷索引的有效性,處理無效的索引的情況。
- 程式碼不能出現編譯警告。
- 使用錯誤傳遞的錯誤處理思想。
- 衛句風格:先處理所有可能發生錯誤的情況,再處理正常情況。
- 巢狀
do-while(0)
巨集:目的是將一組語句變成一個語句,避免被其他if等中斷。
十、效能
- 使用前向宣告代替
#include
指令。Class M;
- 儘量用
++i
代替i++
。即用字首代替字尾運算。 - 儘量在
for
迴圈之前,先寫計算估值表示式。 - 儘量避免在迴圈體內部定義物件。
- 避免物件拷貝,尤其是代價很高的物件拷貝。
- 避免生成臨時物件,尤其是大的臨時物件。
- 注意大尺寸物件陣列。
- 80-20原則。
十一、相容性
- 遵守ANSI C和ISO C++國際標準。
- 確保型別轉換不會丟失資訊。
- 注意雙位元組字元的相容性。
- 注意運算溢位問題。
- 不要假設型別的儲存尺寸。
- 不要假設表示式的運算順序。
- 不要假設函式引數的計算順序。
- 不要假設不同原始檔中靜態或全域性變數的初始化順序。
- 不要依賴編譯器基於實現、未明確或未定義的功能。
- 將所有
#include
的檔名視為大小寫敏感。 - 避免使用全域性變數、靜態變數、函式靜態變數、類靜態變數。在使用靜態庫,動態庫,多執行緒環境時,會導致相容性問題。
- 不要重新實現標準庫函式,如STL已經存在的。
相關文章
- 最全面的 Android 編碼規範指南Android
- 個人總結——全面的『Python編碼規範』Python
- Google C++ 編碼規範(中文版)GoC++
- C++編碼命名規範 -- 目前應用C++
- C# 編碼規範C#
- C++ 序列操作函式最全總結C++函式
- 【IOS】Objective-C編碼規範iOSObject
- C#開發編碼規範C#
- c#編碼規範【轉載】C#
- 最全面的 C++ 資源、框架大全C++框架
- Objective-C開發編碼規範Object
- 編碼規範系列:css規範CSS
- 最強最全面的數倉建設規範指南
- 程式碼規範之前端編寫碼規範前端
- 嵌入式c語言編碼規範C語言
- Ray Wenderlich 的 Objective-C編碼規範Object
- 最全面的JavaScript除錯技巧總結JavaScript除錯
- 公司C++規範學習C++
- CSS編碼規範CSS
- Javascript編碼規範JavaScript
- html編碼規範HTML
- Swift 編碼規範Swift
- PHP編碼規範PHP
- SQL 編碼規範SQL
- 【iOS】iOS開發編碼規範小結iOS
- 一份比較全面的PHP開發編碼規範.PHP
- 自己總結的C#編碼規範--3.特定場景下的命名最佳實踐C#
- iOS開發總結之程式碼規範iOS
- c++程式碼優化總結C++優化
- c函式編寫規範函式
- MySQL操作規範(總結)MySql
- WEB前端編碼規範Web前端
- python編碼規範Python
- 前端安全編碼規範前端
- 前端html編碼規範前端HTML
- Go 編碼規範指南Go
- Go編碼規範指南Go
- android 編碼規範Android