C/C++編程規範
1、文件結構
C/C++程序通常分為兩個文件,一個文件用於保存程序的聲明,一個文件用於保存程序的實現。
1.1 版權和版本的聲明
版權和版本的聲明位於頭文件和定義文件的開頭,主要內容有:
(1)版權信息
(2)文件名稱,標識符,摘要
(3)當前版本號,作者/修改者,完成日期
(4)版本歷史信息
1.2 頭文件的結構
頭文件由三部分內容組成:
1)頭文件開頭處的版權和版本聲明。
2)預處理塊。
3)函數和類結構聲明等。
1.3 定義文件的結構
定義文件有三部分內容:
1) 定義文件開頭處的版權和版本聲明。
2) 對一些頭文件的引用。
3) 程序的實現體(包括數據和代碼)。
1.4 目錄結構
如果一個軟件的頭文件數目比較多(如超過十個),通常應將頭文件和定義文件分別保存於不同的
目錄,以便於維護。
命名規則
共性規則
1、標識符的長度應當符合“min-length && max-information”原則
2、程序中不要出現僅靠大小寫區分的相似的標識符。
3、程序中不要出現標識符完全相同的局部變量和全局變量,盡管兩者的作用域不同而不會
發生語法錯誤,但會使人誤解
4、變量的名字應當使用“名詞”或者“形容詞+名詞”。
5、全局函數的名字應當使用“動詞”或者“動詞+名詞”(動賓詞組)。類的成員函數應
當只使用“動詞”,被省略掉的名詞就是對象本身
6、用正確的反義詞組命名具有互斥意義的變量或相反動作的函數等。
Windows 應用程序命名規則
1、類名和函數名用大寫字母開頭的單詞組合而成
2、變量和參數用小寫字母開頭的單詞組合而成。
3、常量全用大寫的字母,用下劃線分割單詞。
4、靜態變量加前綴s_(表示static)。
5、如果需要定義全局變量,則使全局變量加前綴g_(表示global)。
6、類的數據成員加前綴m_(表示member),這樣可以避免數據成員與成員函數的參數同名。
7、為了防止某一軟件庫中的一些標識符和其它軟件庫中的沖突,可以為各種標識符加上能
反映軟件性質的前綴。例如三維圖形標準OpenGL 的所有庫函數均以gl 開頭,所有常量(或宏定義)均
以GL 開頭
Linux 應用程序函數命名規則
函數命名應遵循下面兩個原則:
1)屬於某一模塊的函數,加上前綴,前綴為模塊縮寫;
2)函數名應該表明函數意義,格式為"前綴_名詞_動詞";
復合表達式
1、不要編寫太復雜的復合表達式。
2、不要有多用途的復合表達式。
3、不要把程序中的復合表達式與“真正的數學表達式”混淆。
if (a < b < c) // a < b < c 是數學表達式而不是程序表達式
並不表示 if ((a<b) && (b<c))
if 語句
1 布爾變量與零值比較
不可將布爾變量直接與TRUE、FALSE 或者1、0 進行比較。
假設布爾變量名字為flag,它與零值比較的標準if 語句如下:
if (flag) // 表示flag 為真
if (!flag) // 表示flag 為假
其它的用法都屬於不良風格,例如:
if (flag == TRUE)
if (flag == 1 )
if (flag == FALSE)
if (flag == 0)
2 整型變量與零值比較
應當將整型變量用“==”或“!=”直接與0 比較。
3 浮點變量與零值比較
不可將浮點變量用“==”或“!=”與任何數字比較。
千萬要留意,無論是float 還是double 類型的變量,都有精度限制。所以一定要避免將浮點變量
用“==”或“!=”與數字比較,應該設法轉化成“>=”或“<=”形式。
假設浮點變量的名字為x,應當將
if (x == 0.0) // 隱含錯誤的比較
轉化為
if ((x>=-EPSINON) && (x<=EPSINON))
其中EPSINON 是允許的誤差(即精度)。
4 指針變量與零值比較
【規則4-3-4】應當將指針變量用“==”或“!=”與NULL 比較。
指針變量的零值是“空”(記為NULL)。盡管NULL 的值與0 相同,但是兩者意義不同。假設指針
變量的名字為p,它與零值比較的標準if 語句如下:
if (p == NULL) // p 與NULL 顯式比較,強調p 是指針變量
if (p != NULL)
不要寫成
if (p == 0) // 容易讓人誤解p 是整型變量
if (p != 0)
或者
if (p) // 容易讓人誤解p 是布爾變量
if (!p)
循環語句的效率
1、在多重循環中,如果有可能,應當將最長的循環放在最內層,最短的循環放在最外層,
以減少CPU 跨切循環層的次數。
2、如果循環體內存在邏輯判斷,並且循環次數很大,宜將邏輯判斷移到循環體的外面。
for 語句的循環控制變量
1、不可在for 循環體內修改循環變量,防止for 循環失去控制。
2、建議for 語句的循環控制變量的取值采用“半開半閉區間”寫法。
switch 語句
1】每個case 語句的結尾不要忘了加break,
2】不要忘記最後那個default 分支。
常量
1 const 與#define 的比較
(1) const 常量有數據類型,而宏常量沒有數據類型。編譯器可以對前者進行類型安全檢查。而對後
者只進行字符替換,沒有類型安全檢查,並且在字符替換可能會產生意料不到的錯誤(邊際效應)。
(2) 有些集成化的調試工具可以對const 常量進行調試,但是不能對宏常量進行調試。
1】盡量使用含義直觀的常量來表示那些將在程序中多次出現的數字或字符串。
2】在C++ 程序中只使用const 常量而不使用宏常量,即const 常量完全取代宏常量
2 常量定義規則
1】需要對外公開的常量放在頭文件中,不需要對外公開的常量放在定義文件的頭部。為便
於管理,可以把不同模塊的常量集中存放在一個公共的頭文件中。
2】如果某一常量與其它常量密切相關,應在定義中包含這種關系,而不應給出一些孤立的
值。
函數設計
一個函數的註釋信息如下例:
/**********************************************************************************
* Function: calculate The area of rectangle *
* parameter: the Length and Width of
rectangle *
* outout: the area of rectangle *
***********************************************************************************/
int GetValue(int iLength,int iWidth)
{
… … ..
return iArea;
}
1 參數的規則
1】參數的書寫要完整,不要貪圖省事只寫參數的類型而省略參數名字。如果函數沒有參數,
則用void 填充。
2】參數命名要恰當,順序要合理。
3】如果參數是指針,且僅作輸入用,則應在類型前加const,以防止該指針在函數體內被意
外修改
4】如果輸入參數以值傳遞的方式傳遞對象,則宜改用“const & ”方式來傳遞,這樣可以省
去臨時對象的構造和析構過程,從而提高效率。
5】參數缺省值只能出現在函數的聲明中,而不能出現在定義體中。
6】如果函數有多個參數,參數只能從後向前挨個兒缺省,否則將導致函數調用語句怪模怪
樣
7】避免函數有太多的參數,參數個數盡量控制在5 個以內。如果參數太多,在使用時容易
將參數類型或順序搞錯。
8】盡量不要使用類型和數目不確定的參數。
2 返回值的規則
1】不要省略返回值的類型。
2】函數名字與返回值類型在語義上不可沖突
3】不要將正常值和錯誤標誌混在一起返回。正常值用輸出參數獲得,而錯誤標誌用return 語
句返回
4】給以“指針傳遞”方式的函數返回值加 const 修飾,那麽函數返回值(即指針)的內容
不能被修改,該返回值只能被賦給加 const 修飾的同類型指針。
5】函數返回值采用“值傳遞方式”,由於函數會把返回值復制到外部臨時的存儲單元中,
加 const 修飾沒有任何價值。
6】函數返回值采用“引用傳遞”的場合並不多,這種方式一般只出現在類的賦值函數中,
目的是為了實現鏈式表達。
3 函數內部實現的規則
1】在函數體的“入口處”,對參數的有效性進行檢查。
2】在函數體的“出口處”,對return 語句的正確性和效率進行檢查。
(1)return 語句不可返回指向“棧內存”的“指針”或者“引用”,因為該內存在函數體結束時被自
動銷毀。
(2)要搞清楚返回的究竟是“值”、“指針”還是“引用”。
(3)如果函數返回值是一個對象,要考慮return 語句的效率。
其它建議
1】函數的功能要單一,不要設計多用途的函數。
2】函數體的規模要小,盡量控制在50 行代碼之內。
3】盡量避免函數帶有“記憶”功能。相同的輸入應當產生相同的輸出。帶有“記憶”功能
的函數,其行為可能是不可預測的,因為它的行為可能取決於某種“記憶狀態”。這樣的函數既不易理
解又不利於測試和維護。在C/C++語言中,函數的static 局部變量是函數的“記憶”存儲器。建議盡量
少用static 局部變量,除非必需
4】不僅要檢查輸入參數的有效性,還要檢查通過其它途徑進入函數體內的變量的有效性,
例如全局變量、文件句柄等。
5】用於出錯處理的返回值一定要清楚,讓使用者不容易忽視或誤解錯誤情況
引用與指針的比較
引用是C++中的概念,容易把引用和指針混淆。
引用的一些規則如下:
(1)引用被創建的同時必須被初始化(指針則可以在任何時候被初始化)。
(2)不能有NULL 引用,引用必須與合法的存儲單元關聯(指針則可以是NULL)。
(3)一旦引用被初始化,就不能改變引用的關系(指針則可以隨時改變所指的對象)
普通函數重載
1】重載函數中的參數不同(包括類型、順序不同),才是重載函數,而僅僅返回值不同
則不行。
2】當心隱式類型轉換導致重載函數產生二義性,數字本身沒有類型,將數字當作參數時
將自動進行類型轉換(稱為隱式類型轉換)。
成員函數的重載、覆蓋與隱藏
1】成員函數的重載、覆蓋(override)與隱藏很容易混淆,註意區分。
2】註意如果派生類的函數與基類的函數同名,但是參數不同。此時,不論有無 virtual
關鍵字,基類的函數將被隱藏(註意別與重載混淆)。
3】註意如果派生類的函數與基類的函數同名,並且參數也相同,但是基類函數沒有
virtual 關鍵字。此時,基類的函數被隱藏(註意別與覆蓋混淆)
內聯函數
1】盡量用內聯取代宏代碼,提高函數的執行效率(速度)。
2】關鍵字 inline 必須與函數定義體放在一起才能使函數成為內聯,僅將 inline 放在函
數聲明前面不起任何作用。
3】如果函數體內的代碼比較長或函數體內出現循環,則不宜使用內聯
內存管理
內存分配方式有三種:
(1) 從靜態存儲區域分配。內存在程序編譯的時候就已經分配好,這塊內存在程序的整個運行期間都
存在。例如全局變量,static 變量
(2) 在棧上創建。在執行函數時,函數內局部變量的存儲單元都可以在棧上創建,函數執行結束時這
些存儲單元自動被釋放。棧棧內存分配運算內置於處理器的指令集中,效率很高,但是分配的內存容量有
限。
(3) 從堆上分配,亦稱動態內存分配。程序在運行的時候用malloc 或new 申請任意多少的內存,程
序員自己負責在何時用free 或delete 釋放內存。動態內存的生存期由我們決定,使用非常靈活,但問
題也最多
1】用malloc 或new 申請內存之後,應該立即檢查指針值是否為NULL。防止使用指針值為NULL
的內存。
2】不要忘記為數組和動態內存賦初值。防止將未被初始化的內存作為右值使用。
3】避免數組或指針的下標越界,特別要當心發生“多1”或者“少1”操作。
4】動態內存的申請與釋放必須配對,防止內存泄漏。
5】用free 或delete 釋放了內存之後,立即將指針設置為NULL,防止產生“野指針”。
類的構造函數
1】“缺省的拷貝構造函數”和“缺省的賦值函數”均采用“位拷貝”而非“值拷貝”的方式來實現,若類中含有指針變量,不能采用缺省的方式
2】如果類存在繼承關系,派生類必須在其初始化表裏調用基類的構造函數。
3】類的 const 常量只能在初始化表裏被初始化,因為它不能在函數體內用賦值的方式來初始化。
4】非內部數據類型的成員對象采用初始化表的方式初始化較好。
5】拷貝構造函數和賦值函數非常容易混淆,常導致錯寫、錯用。拷貝構造函數是在對象被創建時調用的,而賦值函數只能被已經存在了的對象調用
成員函數
1】任何不會修改數據成員的函數都應該聲明為 const 類型
類的繼承和組合
1】如果類 A 和類 B 毫不相關,不可以為了使 B 的功能更多些而讓 B 繼承 A 的功能和屬性
2】若在邏輯上 B 是 A 的“一種情況”,則允許 B 繼承 A 的功能和屬性。
3】若在邏輯上 A 是 B 的“一部分”(a part of),則不允許 B 從 A 派生,而是要用 A 和其它東西組合出 B
提高程序的效率
1】不要一味地追求程序的效率,應當在滿足正確性、可靠性、健壯性、可讀性等質量因素的前提下,設法提高程序的效率。
2】以提高程序的全局效率為主,提高局部效率為輔。
3】在優化程序的效率時,應當先找出限制效率的“瓶頸”,不要在無關緊要之處優化。
4】先優化數據結構和算法,再優化執行代碼。
5】有時候時間效率和空間效率可能對立,此時應當分析那個更重要,作出適當的折衷。例如多花費一些內存來提高性能
6】不要追求緊湊的代碼,因為緊湊的代碼並不能產生高效的機器碼。
C/C++編程規範