天天看點

華為C語言程式設計規範(精華總結)

目錄

1、代碼總體原則

2、頭檔案

2、函數

3、辨別符命名與定義

4、變量

5、宏、常量

6、表達式

7、注釋

8、排版與格式

9、代碼編輯編譯

“編寫程式應該以人為本,計算機第二。”                                                              

                                                                                                                                      ——Steve McConnell

“無緣進華為,但可以用華為的标準要求自己。”                                                              

                                                                                                                                      ——不脫發的程式猿

華為C語言程式設計規範(精華總結)

1、清晰第一

清晰性是易于維護、易于重構的程式必需具備的特征。代碼首先是給人讀的,好的代碼應當可以像文章一樣發聲朗誦出來。

目前軟體維護期成本占整個生命周期成本的40%~90%。根據業界經驗,維護期變更代碼的成本,小型系統是開發期的5倍,大型系統(100萬行代碼以上)可以達到100倍。業界的調查指出,開發組平均大約一半的人力用于彌補過去的錯誤,而不是添加新的功能來幫助公司提高競争力。

一般情況下,代碼的可閱讀性高于性能,隻有确定性能是瓶頸時,才應該主動優化。

2、簡潔為美

簡潔就是易于了解并且易于實作。代碼越長越難以看懂,也就越容易在修改時引入錯誤。寫的代碼越多,意味着出錯的地方越多,也就意味着代碼的可靠性越低。是以,我們提倡大家通過編寫簡潔明了的代碼來提升代碼可靠性。

廢棄的代碼(沒有被調用的函數和全局變量)要及時清除,重複代碼應該盡可能提煉成函數。

3、選擇合适的風格,與代碼原有風格保持一緻

産品所有人共同分享同一種風格所帶來的好處,遠遠超出為了統一而付出的代價。在公司已有編碼規範的指導下,審慎地編排代碼以使代碼盡可能清晰,是一項非常重要的技能。 如果重構/ / 修改其他風格的代碼時,比較明智的做法是根據 現有 代碼 的 現有風格繼續編寫代碼,或者使用格式轉換工具進行轉換成公司内部風格。

對于C語言來說,頭檔案的設計展現了大部分的系統設計。 不合理的頭檔案布局是編譯時間過長的根因,不合理的頭檔案實際上反映了不合理的設計。

1、頭檔案中适合放置接口的聲明,不适合放置實作

頭檔案是子產品(Module)或單元(Unit)的對外接口。頭檔案中應放置對外部的聲明,如對外提供的函數聲明、宏定義、類型定義等。

要求:

内部使用的函數(相當于類的私有方法)聲明不應放在頭檔案中。

内部使用的宏、枚舉、結構定義不應放入頭檔案中。

變量定義不應放在頭檔案中,應放在.c檔案中。

變量的聲明盡量不要放在頭檔案中,亦即盡量不要使用全局變量作為接口。變量是子產品或單元的内部實作細節,不應通過在頭檔案中聲明的方式直接暴露給外部,應通過函數接口的方式進行對外暴露。 即使必須使用全局變量,也隻應當在.c中定義全局變量,在.h中僅聲明變量為全局的。

2、頭檔案應當職責單一,切忌依賴複雜

頭檔案過于複雜,依賴過于複雜是導緻編譯時間過長的主要原因。很多現有代碼中頭檔案過大,職責過多,再加上循環依賴的問題,可能導緻為了在.c中使用一個宏,而包含十幾個頭檔案。

錯誤示例:某平台定義WORD類型的頭檔案:

#include <VXWORKS.H>
#include <KERNELLIB.H>
#include <SEMLIB.H>
#include <INTLIB.H>
#include <TASKLIB.H>
#include <MSGQLIB.H>
#include <STDARG.H>
#include <FIOLIB.H>
#include <STDIO.H>
#include <STDLIB.H>
#include <CTYPE.H>
#include <STRING.H>
#include <ERRNOLIB.H>
#include <TIMERS.H>
#include <MEMLIB.H>
#include <TIME.H>
#include <WDLIB.H>
#include <SYSLIB.H>
#include <TASKHOOKLIB.H>
#include <REBOOTLIB.H>
…
typedef unsigned short WORD;
…      

這個頭檔案不但定義了基本資料類型WORD,還包含了stdio.h syslib.h等等不常用的頭檔案。如果工程中有10000個源檔案,而其中100個源檔案使用了stdio.h的printf,由于上述頭檔案的職責過于龐大,而WORD又是每一個檔案必須包含的,進而導緻stdio.h/syslib.h等可能被不必要的展開了9900次,大大增加了工程的編譯時間。

3、頭檔案應向穩定的方向包含

頭檔案的包含關系是一種依賴,一般來說,應當讓不穩定的子產品依賴穩定的子產品,進而當不穩定的子產品發生變化時,不會影響(編譯)穩定的子產品。

就我們的産品來說,依賴的方向應該是: 産品依賴于平台,平台依賴于标準庫。某産品線平台的代碼中已經包含了産品的頭檔案,導緻平台無法單獨編譯、釋出和測試,是一個非常糟糕的反例。除了不穩定的子產品依賴于穩定的子產品外,更好的方式是兩個子產品共同依賴于接口,這樣任何一個子產品的内部實作更改都不需要重新編譯另外一個子產品。在這裡,我們假設接口本身是最穩定的。

4、每一個 .c 檔案應有一個同名 .h 檔案,用于聲明需要對外公開的接口

如果一個.c檔案不需要對外公布任何接口,則其就不應當存在,除非它是程式的入口,如main函數所在的檔案。

現有某些産品中,習慣一個.c檔案對應兩個頭檔案,一個用于存放對外公開的接口,一個用于存放内部需要用到的定義、聲明等,以控制.c檔案的代碼行數。編者不提倡這種風格。這種風格的根源在于源檔案過大,應首先考慮拆分.c檔案,使之不至于太大。另外,一旦把私有定義、聲明放到獨立的頭檔案中,就無法從技術上避免别人include之,難以保證這些定義最後真的隻是私有的。

5、禁止頭檔案循環依賴

頭檔案循環依賴,指a.h包含b.h,b.h包含c.h,c.h包含a.h之類導緻任何一個頭檔案修改,都導緻所有包含了a.h/b.h/c.h的代碼全部重新編譯一遍。而如果是單向依賴,如a.h包含b.h,b.h包含c.h,而c.h不包含任何頭檔案,則修改a.h不會導緻包含了b.h/c.h的源代碼重新編譯。

6、  .c/.h檔案禁止包含用不到的頭檔案

很多系統中頭檔案包含關系複雜,開發人員為了省事起見,可能不會去一一鑽研,直接包含一切想到的頭檔案,甚至有些産品幹脆釋出了一個god.h,其中包含了所有頭檔案,然後釋出給各個項目組使用,這種隻圖一時省事的做法,導緻整個系統的編譯時間進一步惡化,并對後來人的維護造成了巨大的麻煩。

7、  頭檔案應當自包含

簡單的說,自包含就是任意一個頭檔案均可獨立編譯。如果一個檔案包含某個頭檔案,還要包含另外一個頭檔案才能工作的話,就會增加交流障礙,給這個頭檔案的使用者增添不必要的負擔。

示例:如果a.h不是自包含的,需要包含b.h才能編譯,會帶來的危害:每個使用a.h頭檔案的.c檔案,為了讓引入的a.h的内容編譯通過,都要包含額外的頭檔案b.h。額外的頭檔案b.h必須在a.h之前進行包含,這在包含順序上産生了依賴。

注意:該規則需要與“.c/.h檔案禁止包含用不到的頭檔案”規則一起使用,不能為了讓a.h自包含,而在a.h中包含不必要的頭檔案。a.h要剛剛可以自包含,不能在a.h中多包含任何滿足自包含之外的其他頭檔案。

8、總是編寫内部 #include 保護符( #define  保護)

多次包含一個頭檔案可以通過認真的設計來避免。如果不能做到這一點,就需要采取阻止頭檔案内容被包含多于一次的機制。通常的手段是為每個檔案配置一個宏,當頭檔案第一次被包含時就定義這個宏,并在頭檔案被再次包含時使用它以排除檔案内容。所有頭檔案都應當使用#define 防止頭檔案被多重包含,命名格式為FILENAME_H,為了保證唯一性,更好的命名是PROJECTNAME_PATH_FILENAME_H。

注:沒有在宏最前面加上單下劃線"_",是因為一般以單下劃線"_"和雙下劃線"__"開頭的辨別符為ANSIC等使用,在有些靜态檢查工具中,若全局可見的辨別符以"_"開頭會給出告警。

定義包含保護符時,應該遵守如下規則:

保護符使用唯一名稱;

不要在受保護部分的前後放置代碼或者注釋。

正确示例:假定VOS工程的timer子產品的timer.h,其目錄為VOS/include/timer/timer.h,應按如下方式保護:

#ifndef VOS_INCLUDE_TIMER_TIMER_H
#define VOS_INCLUDE_TIMER_TIMER_H
...
#endif
也可以使用如下簡單方式保護:
#ifndef TIMER_H
#define TIMER_H
...
#endif      

例外情況:頭檔案的版權聲明部分以及頭檔案的整體注釋部分(如闡述此頭檔案的開發背景、使用注意事項等)可以放在保護符(#ifndef XX_H)前面。

9、禁止在頭檔案中定義變量

在頭檔案中定義變量,将會由于頭檔案被其他.c檔案包含而導緻變量重複定義。

10、隻能通過包含頭檔案的方式使用其他 .c 提供的接口,禁止在.c 中通過 extern 的方式使用外部函數接口、變量

若a.c使用了b.c定義的foo()函數,則應當在b.h中聲明extern int foo(int input);并在a.c中通過#include <b.h>來使用foo。禁止通過在a.c中直接寫extern int foo(int input);來使用foo,後面這種寫法容易在foo改變時可能導緻聲明和定義不一緻。

11、禁止在 extern "C" 中包含頭檔案

在extern "C"中包含頭檔案,會導緻extern "C"嵌套,Visual Studio對extern "C"嵌套層次有限制,嵌套層次太多會編譯錯誤。

在extern "C"中包含頭檔案,可能會導緻被包含頭檔案的原有意圖遭到破壞。

錯誤示例:

extern “C”
{
#include “xxx.h”
...
}
正确示例:
#include “xxx.h”
extern “C”
{
...
}      

12、一個子產品通常包含多個 .c 檔案,建議放在同一個目錄下,目錄名即為子產品名。為友善外部使用者,建議每一個子產品提供一個 .h ,檔案名為目錄名

需要注意的是,這個.h并不是簡單的包含所有内部的.h,它是為了子產品使用者的友善,對外整體提供的子產品接口。以Google test(簡稱GTest)為例,GTest作為一個整體對外提供C++單元測試架構,其1.5版本的gtest工程下有6個源檔案和12個頭檔案。但是它對外隻提供一個gtest.h,隻要包含gtest.h即可使用GTest提供的所有對外提供的功能,使用者不必關系GTest内部各個檔案的關系,即使以後GTest的内部實作改變了,比如把一個源檔案c拆成兩個源檔案,使用者也不必關心,甚至如果對外功能不變,連重新編譯都不需要。對于有些子產品,其内部功能相對松散,可能并不一定需要提供這個.h,而是直接提供各個子子產品或者.c的頭檔案。

比如産品普遍使用的VOS,作為一個大子產品,其内部有很多子子產品,他們之間的關系相對比較松散,就不适合提供一個vos.h。而VOS的子子產品,如Memory(僅作舉例說明,與實際情況可能有所出入),其内部實作高度内聚,雖然其内部實作可能有多個.c和.h,但是對外隻需要提供一個Memory.h聲明接口。

13、如果一個子產品包含多個子子產品,則建議每一個子子產品提供一個對外的 .h,檔案名為子子產品名

降低接口使用者的編寫難度

14、頭檔案不要使用非習慣用法的擴充名,如 .inc

目前很多産品中使用了.inc作為頭檔案擴充名,這不符合c語言的習慣用法。在使用.inc作為頭檔案擴充名的産品,習慣上用于辨別此頭檔案為私有頭檔案。但是從産品的實際代碼來看,這一條并沒有被遵守,一個.inc檔案被多個.c包含比比皆是。

除此之外,使用.inc還導緻source insight、Visual stduio等IDE工具無法識别其為頭檔案,導緻很多功能不可用,如“跳轉到變量定義處”。雖然可以通過配置,強迫IDE識别.inc為頭檔案,但是有些軟體無法配置,如Visual Assist隻能識别.h而無法通過配置識别.inc。

15、同一産品統一包含頭檔案排列方式

常見的包含頭檔案排列方式:功能塊排序、檔案名升序、穩定度排序。

正确示例1:以升序方式排列頭檔案可以避免頭檔案被重複包含:

#include <a.h>
#include <b.h>
#include <c/d.h>
#include <c/e.h>
#include <f.h>      

正确示例2:以穩定度排序,建議将不穩定的頭檔案放在前面,如把産品的頭檔案放在平台的頭檔案前面:

#include <product.h>
#include <platform.h>      

相對來說,product.h修改的較為頻繁,如果有錯誤,不必編譯platform.h就可以發現product.h的錯誤,可以部分減少編譯時間。

函數設計的精髓:編寫整潔函數,同時把代碼有效組織起來。

整潔函數要求:代碼簡單直接、不隐藏設計者的意圖、用幹淨利落的抽象和直截了當的控制語句将函數有機組織起來。

代碼的有效組織包括:邏輯層組織和實體層組織兩個方面。邏輯層,主要是把不同功能的函數通過某種聯系組織起來,主要關注子產品間的接口,也就是子產品的架構。實體層,無論使用什麼樣的目錄或者名字空間等,需要把函數用一種标準的方法組織起來。例如:設計良好的目錄結構、函數名字、檔案組織等,這樣可以友善查找。

1、一個函數僅完成一件功能

一個函數實作多個功能給開發、使用、維護都帶來很大的困難。

将沒有關聯或者關聯很弱的語句放到同一函數中,會導緻函數職責不明确,難以了解,難以測試和改動。

2、重複代碼應該盡可能提煉成函數

重複代碼提煉成函數可以帶來維護成本的降低。

重複代碼是我司不良代碼最典型的特征之一。在“代碼能用就不改”的指導原則之下,大量的煙囪式設計及其實作充斥着各産品代碼之中。新需求增加帶來的代碼拷貝和修改,随着時間的遷移,産品中堆砌着許多類似或者重複的代碼。

項目組應當使用代碼重複度檢查工具,在持續內建環境中持續檢查代碼重複度名額變化趨勢,并對新增重複代碼及時重構。當一段代碼重複兩次時,即應考慮消除重複,當代碼重複超過三次時,應當立刻着手消除重複。

3、避免函數過長,新增函數不超過 50 行 (非空非注釋行)

過長的函數往往意味着函數功能不單一,過于複雜。

函數的有效代碼行數,即NBNC(非空非注釋行)應當在[1,50]區間。

例外:某些實作算法的函數,由于算法的聚合性與功能的全面性,可能會超過50行。

延伸閱讀材料: 業界普遍認為一個函數的代碼行不要超過一個螢幕,避免來回翻頁影響閱讀;一般的代碼度量工具建議都對此進行檢查,例如Logiscope的函數度量:"Number of Statement" (函數中的可執行語句數)建議不超過20行,QA C建議一個函數中的所有行數(包括注釋和空白行)不超過50行。

4、避免函數的代碼塊嵌套過深,新增函數的代碼塊嵌套不超過4層

函數的代碼塊嵌套深度指的是函數中的代碼控制塊(例如:if、for、while、switch等)之間互相包含的深度。每級嵌套都會增加閱讀代碼時的腦力消耗,因為需要在腦子裡維護一個“棧”(比如,進入條件語句、進入循環„„)。應該做進一步的功能分解,進而避免使代碼的閱讀者一次記住太多的上下文。優秀代碼參考值:[1, 4]。

錯誤示例:代碼嵌套深度為5層:

void serial (void)
{
    if (!Received)
    {
        TmoCount = 0;
         switch (Buff)
        {
            case AISGFLG:
                if ((TiBuff.Count > 3)&& ((TiBuff.Buff[0] == 0xff) || (TiBuf.Buff[0] == CurPa.ADDR)))
                {
                    Flg7E = false;
                    Received = true;
                }
                else
                {
                    TiBuff.Count = 0;
                    Flg7D = false;
                    Flg7E = true;
                }
                break;
            default:
                break;
        }
    }
}      

5、 可重入函數應避免使用共享變量;若需要使用,則應通過互斥手段(關中斷、信号量)對其加以保護

可重入函數是指可能被多個任務并發調用的函數。在多任務作業系統中,函數具有可重入性是多個任務可以共用此函數的必要條件。共享變量指的全局變量和static變量。編寫C語言的可重入函數時,不應使用static局部變量,否則必須經過特殊處理,才能使函數具有可重入性。

示例:函數square_exam傳回g_exam平方值。那麼如下函數不具有可重入性。

int g_exam;
unsigned int example( int para )
{
    unsigned int temp;
    g_exam = para; // (**)
    temp = square_exam ( );
    return temp;
}      

此函數若被多個線程調用的話,其結果可能是未知的,因為當(**)語句剛執行完後,另外一個使用本函數的線程可能正好被激活,那麼當新激活的線程執行到此函數時,将使g_exam賦于另一個不同的para值,是以當控制重新回到“temp =square_exam ( )”後,計算出的temp很可能不是預想中的結果。此函數應如下改進。

int g_exam;
unsigned int example( int para )
{
    unsigned int temp;
    [申請信号量操作] // 若申請不到“信号量”,說明另外的程序正處于
    g_exam = para; //給g_exam指派并計算其平方過程中(即正在使用此
    temp = square_exam( ); // 信号),本程序必須等待其釋放信号後,才可繼
    [釋放信号量操作] // 續執行。其它線程必須等待本線程釋放信号量後
    // 才能再使用本信号。
    return temp;
}      

6、對參數的合法性檢查,由調用者負責還是由接口函數負責,應在項目組/子產品内應統一規定。預設由調用者負責。

對于子產品間接口函數的參數的合法性檢查這一問題,往往有兩個極端現象,即:要麼是調用者和被調用者對參數均不作合法性檢查,結果就遺漏了合法性檢查這一必要的處理過程,造成問題隐患;要麼就是調用者和被調用者均對參數進行合法性檢查,這種情況雖不會造成問題,但産生了備援代碼,降低了效率。

7、對函數的錯誤傳回碼要全面處理

一個函數(标準庫中的函數/第三方庫函數/使用者定義的函數)能夠提供一些訓示錯誤發生的方法。這可以通過使用錯誤标記、特殊的傳回資料或者其他手段,不管什麼時候函數提供了這樣的機制,調用程式應該在函數傳回時立刻檢查錯誤訓示。

8、設計高扇入,合理扇出(小于7)的函數

扇出是指一個函數直接調用(控制)其它函數的數目,而扇入是指有多少上級函數調用它。

華為C語言程式設計規範(精華總結)

扇出過大,表明函數過分複雜,需要控制和協調過多的下級函數;而扇出過小,例如:總是1,表明函數的調用層次可能過多,這樣不利于程式閱讀和函數結構的分析,并且程式運作時會對系統資源如堆棧空間等造成壓力。通常函數比較合理的扇出(排程函數除外)通常是3~5。

扇出太大,一般是由于缺乏中間層次,可适當增加中間層次的函數。扇出太小,可把下級函數進一步分解多個函數,或合并到上級函數中。當然分解或合并函數時,不能改變要實作的功能,也不能違背函數間的獨立性。扇入越大,表明使用此函數的上級函數越多,這樣的函數使用效率高,但不能違背函數間的獨立性而單純地追求高扇入。公共子產品中的函數及底層函數應該有較高的扇入。

較良好的軟體結構通常是頂層函數的扇出較高,中層函數的扇出較少,而底層函數則扇入到公共子產品中。

9、廢棄代碼(沒有被調用的函數和變量) ) 要及時清除

程式中的廢棄代碼不僅占用額外的空間,而且還常常影響程式的功能與性能,很可能給程式的測試、維護等造成不必要的麻煩。

10、函數不變參數使用const

不變的值更易于了解/跟蹤和分析,把const作為預設選項,在編譯時會對其進行檢查,使代碼更牢固/更安全。

正确示例:C99标準 7.21.4.4 中strncmp 的例子,不變參數聲明為const。

int strncmp(const char *s1, const char *s2, register size_t n)
{
    register unsigned char u1, u2;
    while (n-- > 0)
    {
        u1 = (unsigned char) *s1++;
        u2 = (unsigned char) *s2++;
        if (u1 != u2)
        {
            return u1 - u2;
        }
        if (u1 == '\0')
        {
            return 0;
        }
    }
    return 0;
}      

11、函數應避免使用全局變量、靜态局部變量和 I/O 操作,不可避免的地方應集中使用

帶有内部“存儲器”的函數的功能可能是不可預測的,因為它的輸出可能取決于内部存儲器(如某标記)的狀态。這樣的函數既不易于了解又不利于測試和維護。在C語言中,函數的static局部變量是函數的内部存儲器,有可能使函數的功能不可預測。

錯誤示例:如下函數,其傳回值(即功能)是不可預測的。

unsigned int integer_sum( unsigned int base )
{
    unsigned int index;
    static unsigned int sum = 0;// 注意,是static類型的。
    // 若改為auto類型,則函數即變為可預測。
    for (index = 1; index <= base; index++)
    {
        sum += index;
    }
    return sum;
}      

12、檢查函數所有非參數輸入的有效性,如資料檔案、公共變量等

函數的輸入主要有兩種:一種是參數輸入;另一種是全局變量、資料檔案的輸入,即非參數輸入。函數在使用輸入參數之前,應進行有效性檢查。

13、 函數的參數個數不超過5個

函數的參數過多,會使得該函數易于受外部(其他部分的代碼)變化的影響,進而影響維護工作。函數的參數過多同時也會增大測試的工作量。

函數的參數個數不要超過5個,如果超過了建議拆分為不同函數。

14、除列印類函數外,不要使用可變長參函數。

可變長參函數的處理過程比較複雜容易引入錯誤,而且性能也比較低,使用過多的可變長參函數将導緻函數的維護難度大大增加。

15、在源檔案範圍内聲明和定義的所有函數,除非外部可見,否則應該增加static關鍵字

如果一個函數隻是在同一檔案中的其他地方調用,那麼就用static聲明。使用static確定隻是在聲明它的檔案中是可見的,并且避免了和其他檔案或庫中的相同辨別符發生混淆的可能性。

正确示例:建議定義一個STATIC宏,在調試階段,将STATIC定義為static,版本釋出時,改為空,以便于後續的打熱更新檔等操作。

#ifdef _DEBUG
#define STATIC static
#else
#define STATIC
#endif      

辨別符的命名規則曆來是一個敏感話題,典型的命名風格如unix風格、windows風格等,從來無法達成共識。實際上,各種風格都有其優勢也有其劣勢,而且往往和個人的審美觀有關。我們對辨別符定義主要是為了讓團隊的代碼看起來盡可能統一,有利于代碼的後續閱讀和修改,産品可以根據自己的實際需要指定命名風格,規範中不再做統一的規定。

1、辨別符的命名要清晰、明了,有明确含義,同時使用完整的單詞或大家基本可以了解的縮寫,避免使人産生誤解

盡可能給出描述性名稱,不要節約空間,讓别人很快了解你的代碼更重要。

正确示例:

int error_number;
int number_of_completed_connection;      
int n;
int nerr;
int n_comp_conns;      

2、除了常見的通用縮寫以外,不使用單詞縮寫,不得使用漢語拼音

較短的單詞可通過去掉“元音”形成縮寫,較長的單詞可取單詞的頭幾個字母形成縮寫,一些單詞有大家公認的縮寫,常用單詞的縮寫必須統一。協定中的單詞的縮寫與協定保持一緻。對于某個系統使用的專用縮寫應該在注視或者某處做統一說明。

正确示例:一些常見可以縮寫的例子:

argument 可縮寫為 arg
buffer 可縮寫為 buff
clock 可縮寫為 clk
command 可縮寫為 cmd
compare 可縮寫為 cmp
configuration 可縮寫為 cfg
device 可縮寫為 dev
error 可縮寫為 err
hexadecimal 可縮寫為 hex
increment 可縮寫為 inc
initialize 可縮寫為 init
maximum 可縮寫為 max
message 可縮寫為 msg
minimum 可縮寫為 min
parameter 可縮寫為 para
previous 可縮寫為 prev
register 可縮寫為 reg
semaphore 可縮寫為 sem
statistic 可縮寫為 stat
synchronize 可縮寫為 sync
temp 可縮寫為 tmp      

3、産品/項目組内部應保持統一的命名風格

Unix like和windows like風格均有其擁趸,産品應根據自己的部署平台,選擇其中一種,并在産品内部保持一緻。

4、用正确的反義詞組命名具有互斥意義的變量或相反動作的函數等

add/remove begin/end create/destroy
insert/delete first/last get/release
increment/decrement put/get add/delete
lock/unlock open/close min/max
old/new start/stop  next/previous
source/target show/hide  send/receive
source/destination copy/paste up/down      

5、盡量避免名字中出現數字編号,除非邏輯上的确需要編号

錯誤示例:如下命名,使人産生疑惑。

#define EXAMPLE_0_TEST_
#define EXAMPLE_1_TEST_      

正确示例:應改為有意義的單詞命名。

#define EXAMPLE_UNIT_TEST_
#define EXAMPLE_ASSERT_TEST_      

6、辨別符前不應添加子產品、項目、産品、部門的名稱作為字首

很多已有代碼中已經習慣在檔案名中增加子產品名,這種寫法類似匈牙利命名法,導緻檔案名不可讀,并且帶來帶來如下問題:

第一眼看到的是子產品名,而不是真正的檔案功能,阻礙閱讀;

檔案名太長;

檔案名和子產品綁定,不利于維護和移植。若foo.c進行重構後,從a子產品挪到b子產品,若foo.c

中有子產品名,則需要将檔案名從a_module_foo.c改為b_module_foo.c。

7、平台/ / 驅動等适配代碼的辨別符命名風格保持和平台

涉及到外購晶片以及配套的驅動,這部分的代碼變動(包括為産品做适配的新增代碼),應該保持原有的風格。

8、重構/修改部分代碼時,應保持和原有代碼的命名風格一緻

根據源代碼現有的風格繼續編寫代碼,有利于保持總體一緻。

9、檔案命名統一采用小寫字元

因為不同系統對檔案名大小寫處理會不同(如MS的DOS、Windows系統不區分大小寫,但是Linux系統則區分),是以代碼檔案命名建議統一采用全小寫字母命名。

10、全局變量應增加“g_” 字首,靜态變量應增加“s_”

首先,全局變量十分危險,通過字首使得全局變量更加醒目,促使開發人員對這些變量的使用更加小心。

其次,從根本上說,應當盡量不使用全局變量,增加g_和s_字首,會使得全局變量的名字顯得很醜陋,進而促使開發人員盡量少使用全局變量。

11、禁止使用單位元組命名變量,但 允許 定義i 、j、k作為局部循環變量

12、 不建議使用匈牙利命名法

匈牙利命名法是一種程式設計時的命名規範。基本原則是:變量名=屬性+類型+對象描述。匈牙利命名法源于微軟,然而卻被很多人以訛傳訛的使用。而現在即使是微軟也不再推薦使用匈牙利命名法。曆來對匈牙利命名法的一大诟病,就是導緻了變量名難以閱讀,這和本規範的指導思想也有沖突,是以本規範特意強調,變量命名不應采用匈牙利命名法,而應該想法使變量名為一個有意義的詞或詞組,友善代碼的閱讀。

變量命名需要說明的是變量的含義,而不是變量的類型。在變量命名前增加類型說明,反而降低了變量的可讀性;更麻煩的問題是,如果修改了變量的類型定義,那麼所有使用該變量的地方都需要修改。

13、使用名詞或者形容詞+名詞方式命名變量

14、函數命名應以函數要執行的動作命名,一般采用動詞或者動詞+名詞的結構

正确示例:找到目前程序的目前目錄:

DWORD GetCurrentDirectory( DWORD BufferLength, LPTSTR Buffer );      

15、函數指針除了字首,其他按照函數的命名規則命名

16、對于數值或者字元串等等常量的定義,建議采用全大寫字母,單詞之間加下劃線“_”的方式命名(枚舉同樣建議使用此方式定義)

#define PI_ROUNDED 3.14      

17、除了頭檔案或編譯開關等特殊辨別定義,宏定義不能使用下劃線“_”開頭和結尾

一般來說,‟_‟開頭、結尾的宏都是一些内部的定義,ISO/IEC 9899(俗稱C99)中有如下的描述(6.10.8 Predefined macro names):

None of these macro names (這裡上面是一些内部定義的宏的描述),nor the identifier defined,shall be the subject of a #define or a #undef preprocessing directive.Any other predefined macro names shall begin with a leading underscore fol lowedby an uppercase letter ora second underscore.

1、一個變量隻有一個功能,不能把一個變量用作多種用途

一個變量隻用來表示一個特定功能,不能把一個變量作多種用途,即同一變量取值不同時,其代表的意義也不同。

錯誤示例:具有兩種功能的反例

WORD DelRelTimeQue( void )
{
    WORD Locate;
    Locate = 3; 
    Locate = DeleteFromQue(Locate); /* Locate具有兩種功能:位置和函數DeleteFromQue的傳回值 */
    return Locate;
}      

正确做法:使用兩個變量

WORD DelRelTimeQue( void )
{
    WORD Ret;
    WORD Locate;
    Locate = 3;
    Ret  = DeleteFromQue(Locate);
    return Ret;
}      

2、結構功能單一,不要設計面面俱到的資料結構

相關的一組資訊才是構成一個結構體的基礎,結構的定義應該可以明确的描述一個對象,而不是一組相關性不強的資料的集合。設計結構時應力争使結構代表一種現實事務的抽象,而不是同時代表多種。結構中的各元素應代表同一事務的不同側面,而不應把描述沒有關系或關系很弱的不同僚務的元素放到同一結構中。

錯誤示例:如下結構不太清晰、合理。

typedef struct STUDENT_STRU
{
    unsigned char name[32]; /* student's name */
    unsigned char age; /* student's age */
    unsigned char sex; /* student's sex, as follows */
    /* 0 - FEMALE; 1 - MALE */
    unsigned char teacher_name[32]; /* the student teacher's name */
    unsigned char teacher_sex; /* his teacher sex */
} STUDENT;      

正确示例:若改為如下,會更合理些。

typedef struct TEACHER_STRU
{
    unsigned char name[32]; /* teacher name */
    unsigned char sex; /* teacher sex, as follows */
    /* 0 - FEMALE; 1 - MALE */
    unsigned int teacher_ind; /* teacher index */
} TEACHER;
typedef struct STUDENT_STRU
{
    unsigned char name[32]; /* student's name */
    unsigned char age; /* student's age */
    unsigned char sex; /* student's sex, as follows */
    /* 0 - FEMALE; 1 - MALE */
    unsigned int teacher_ind; /* his teacher index */
} STUDENT;      

3、不用或者少用全局變量

單個檔案内部可以使用static的全局變量,可以将其了解為類的私有成員變量。

全局變量應該是子產品的私有資料,不能作用對外的接口使用,使用static類型定義,可以有效防止外部檔案的非正常通路,建議定義一個STATIC宏,在調試階段,将STATIC定義為static,版本釋出時,改為空,以便于後續的打更新檔等操作。

4、防止局部變量與全局變量同名

盡管局部變量和全局變量的作用域不同而不會發生文法錯誤,但容易使人誤解。

5、通訊過程中使用的結構,必須注意位元組序

通訊封包中,位元組序是一個重要的問題,我司裝置使用的CPU類型複雜多樣,大小端、32位/64位的處理器也都有,如果結構會在封包互動過程中使用,必須考慮位元組序問題。由于位域在不同位元組序下,表現看起來差别更大,是以更需要注意對于這種跨平台的互動,資料成員發送前,都應該進行主機序到網絡序的轉換;接收時,也必須進行網絡序到主機序的轉換。

6、嚴禁使用未經初始化的變量作為右值

在首次使用前初始化變量,初始化的地方離使用的地方越近越好。

7、構造僅有一個子產品或函數可以修改、建立,而其餘有關子產品或函數隻通路的全局變量,防止多個不同子產品或函數都可以修改、建立同一全局變量的現象

降低全局變量耦合度。

8、使用面向接口程式設計思想,通過 API 通路資料:如果本子產品的資料需要對外部子產品開放 ,應提供接口函數來設定、擷取,同時注意全局資料的通路互斥

避免直接暴露内部資料給外部模型使用,是防止子產品間耦合最簡單有效的方法。定義的接口應該有比較明确的意義,比如一個風扇管理功能子產品,有自動和手動工作模式,那麼設定、查詢工作子產品就可以定義接口為SetFanWorkMode,GetFanWorkMode;查詢轉速就可以定義為GetFanSpeed;風扇支援節能功能開關,可以定義EnabletFanSavePower等。

9、明确全局變量的初始化順序,避免跨子產品的初始化依賴

系統啟動階段,使用全局變量前,要考慮到該全局變量在什麼時候初始化,使用全局變量和初始化全局變量,兩者之間的時序關系,誰先誰後,一定要分析清楚,不然後果往往是低級而又災難性的。

10、盡量減少沒有必要的資料類型預設轉換與強制轉換

當進行資料類型強制轉換時,其資料的意義、轉換後的取值等都有可能發生變化,而這些細節若考慮不周,就很有可能留下隐患。

錯誤示例:如下指派,多數編譯器不産生告警,但值的含義還是稍有變化。

char ch;
unsigned short int exam;
ch = -1;
exam = ch; // 編譯器不産生告警,此時exam為0xFFFF。      

1、用宏定義表達式時,要使用完備的括号

因為宏隻是簡單的代碼替換,不會像函數一樣先将參數計算後,再傳遞。

錯誤示例:如下定義的宏都存在一定的風險

#define RECTANGLE_AREA(a, b) a * b
#define RECTANGLE_AREA(a, b) (a * b)
#define RECTANGLE_AREA(a, b) (a) * (b)      

#define RECTANGLE_AREA(a, b) ((a) * (b))

這是因為:如果定義 #define RECTANGLE_AREA(a, b) a * b  或 #define RECTANGLE_AREA(a, b) (a * b)則 c/RECTANGLE_AREA(a, b)  将擴充成 c/a * b , c  與 b 本應該是除法運算,結果變成了乘法運算,造成錯誤。

如果定義 #define RECTANGLE_AREA(a, b) (a * b)則 RECTANGLE_AREA(c + d, e + f) 将擴充成: (c + d * e + f), d 與 e 先運算,造成錯誤。

2、将宏所定義的多條表達式放在大括号中

3、使用宏時,不允許參數發生變化

#define SQUARE(a) ((a) * (a))
int a = 5;
int b;
b = SQUARE(a++); // 結果:a = 7,即執行了兩次增。      
b = SQUARE(a);
a++; // 結果:a = 6,即隻執行了一次增。      

同時也建議即使函數調用,也不要在參數中做變量變化操作,因為可能引用的接口函數,在某個版本更新後,變成了一個相容老版本所做的一個宏,結果可能不可預知。

4、不允許直接使用魔鬼數字

使用魔鬼數字的弊端:代碼難以了解;如果一個有含義的數字多處使用,一旦需要修改這個數值,代價慘重。

使用明确的實體狀态或實體意義的名稱能增加資訊,并能提供單一的維護點。

解決途徑:對于局部使用的唯一含義的魔鬼數字,可以在代碼周圍增加說明注釋,也可以定義局部const變量,變量命名自注釋。對于廣泛使用的數字,必須定義const全局變量/宏;同樣變量/宏命名應是自注釋的。0作為一個特殊的數字,作為一般預設值使用沒有歧義時,不用特别定義。

5、除非必要,應盡可能使用函數代替宏

宏對比函數,有一些明顯的缺點:

宏缺乏類型檢查,不如函數調用檢查嚴格;

宏展開可能會産生意想不到的副作用,如#define SQUARE(a) (a) * (a)這樣的定義,如果是SQUARE(i++),就會導緻i被加兩次;如果是函數調用double square(double a) {return a * a;}則不會有此副作用;

以宏形式寫的代碼難以調試難以打斷點,不利于定位問題;

宏如果調用的很多,會造成代碼空間的浪費,不如函數空間效率高。

錯誤示例:下面的代碼無法得到想要的結果:

#define MAX_MACRO(a, b) ((a) > (b) ? (a) : (b))
int MAX_FUNC(int a, int b) {
    return ((a) > (b) ? (a) : (b));
}
int testFunc()
{
    unsigned int a = 1;
    int b = -1;
    printf("MACRO: max of a and b is: %d\n", MAX_MACRO(++a, b));
    printf("FUNC : max of a and b is: %d\n", MAX_FUNC(a, b));
    return 0;
}      

上面宏代碼調用中,由于宏缺乏類型檢查,a和b的比較變成無符号數的比較,結果是a < b,是以a隻加了一次,是以最終的輸出結果是:

MACRO: max of a and b is: -1
FUNC : max of a and b is: 2      

6、常量建議使用 const 定義代替宏

“盡量用編譯器而不用預處理”,因為#define經常被認為好象不是語言本身的一部分。看下面的語句:

#define ASPECT_RATIO 1.653      

編譯器會永遠也看不到ASPECT_RATIO這個符号名,因為在源碼進入編譯器之前,它會被預處理程式去掉,于是ASPECT_RATIO不會加入到符号清單中。如果涉及到這個常量的代碼在編譯時報錯,就會很令人費解,因為報錯資訊指的是1.653,而不是ASPECT_RATIO。如果ASPECT_RATIO不是在你自己寫的頭檔案中定義的,你就會奇怪1.653是從哪裡來的,甚至會花時間跟蹤下去。這個問題也會出現在符号調試器中,因為同樣地,你所寫的符号名不會出現在符号清單中。

解決這個問題的方案很簡單:不用預處理宏,定義一個常量:

const double ASPECT_RATIO = 1.653;      

這種方法很有效,但有兩個特殊情況要注意。首先,定義指針常量時會有點不同。因為常量定義一般是放在頭檔案中(許多源檔案會包含它),除了指針所指的類型要定義成const外,重要的是指針也經常要定義成const。例如,要在頭檔案中定義一個基于char*的字元串常量,你要寫兩次const:

const char * const authorName = "Scott Meyers";      

延伸閱讀材料:關于const和指針的使用,這裡摘錄兩段ISO/IEC 9899(俗稱C99)的描述:

7、宏定義中盡量不使用 return 、 goto 、 continue 、 break等改變程式流程的語句

如果在宏定義中使用這些改變流程的語句,很容易引起資源洩漏問題,使用者很難自己察覺。

錯誤示例:在某頭檔案中定義宏CHECK_AND_RETURN:

#define CHECK_AND_RETURN(cond, ret) {if (cond == NULL_PTR) {return ret;}}
//然後在某函數中使用(隻說明問題,代碼并不完整):
pMem1 = VOS_MemAlloc(...);
CHECK_AND_RETURN(pMem1 , ERR_CODE_XXX)
pMem2 = VOS_MemAlloc(...);
CHECK_AND_RETURN(pMem2 , ERR_CODE_XXX) /*此時如果pMem2==NULL_PTR,則pMem1未釋放函數就傳回了,造成記憶體洩漏。*/      

是以說,類似于CHECK_AND_RETURN這些宏,雖然能使代碼簡潔,但是隐患很大,使用須謹慎。

1、表達式的值在标準所允許的任何運算次序下都應該是相同的

2、函數調用不要作為另一個函數的參數使用,否則對于代碼的調試、閱讀都不利

錯誤示例:如下代碼不合理,僅用于說明當函數作為參數時,由于參數壓棧次數不是代碼可以控制的,可能造成未知的輸出:

int g_var;
int fun1()
{
    g_var += 10;
    return g_var;
}
int fun2()
{
    g_var += 100;
    return g_var;
}
int main(int argc, char *argv[], char *envp[])
{
    g_var = 1;
    printf("func1: %d, func2: %d\n", fun1(), fun2());
    g_var = 1;
    printf("func2: %d, func1: %d\n", fun2(), fun1());
}      

上面的代碼,使用斷點調試起來也比較麻煩,閱讀起來也不舒服,是以不要為了節約代碼行,而寫這種代碼。

3、指派語句不要寫在 if 等語句中,或者作為函數的參數使用

因為if語句中,會根據條件依次判斷,如果前一個條件已經可以判定整個條件,則後續條件語句不會再運作,是以可能導緻期望的部分指派沒有得到運作。

int main(int argc, char *argv[], char *envp[])
{
    int a = 0;
    int b;
    if ((a == 0) || ((b = fun1()) > 10))
    {
        printf("a: %d\n", a);
    }
    printf("b: %d\n", b);
}      

作用函數參數來使用,參數的壓棧順序不同可能導緻結果未知。

4、用括号明确表達式的操作順序,避免過分依賴預設優先級

使用括号強調所使用的操作符,防止因預設的優先級與設計思想不符而導緻程式出錯;同時使得代碼更為清晰可讀,然而過多的括号會分散代碼使其降低了可讀性。

5、指派操作符不能使用在産生布爾值的表達式上

示例:

x = y;
if (x != 0)
{
    foo ();
}
不能寫成:
if (( x = y ) != 0)
{
    foo ();
}      

或者更壞的:

if (x = y)
{
    foo ();
}      

1、優秀的代碼可 以自我解釋,不通過注釋即可輕易讀懂

優秀的代碼不寫注釋也可輕易讀懂,注釋無法把糟糕的代碼變好,需要很多注釋來解釋的代碼往往存在壞味道,需要重構。

錯誤示例:注釋不能消除代碼的壞味道:

重構代碼後,不需要注釋:

int IsPrimeNumber(int num)
{
    int sqrt_of_num = sqrt (num);
    for (int i = 2; i <= sqrt_of_num; i++)
    {
        if (num % i == 0)
        {
            return FALSE;
        }
    }
    return TRUE;
}      

2、注釋的内容要清楚、明了,含義準确,防止注釋二義性

有歧義的注釋反而會導緻維護者更難看懂代碼,正如帶兩塊表反而不知道準确時間。

3、在代碼的功能、意圖層次上進行注釋,即注釋解釋 代碼難以直接表達的意圖 , 而不是重複描述代碼

注釋的目的是解釋代碼的目的、功能和采用的方法,提供代碼以外的資訊,幫助讀者了解代碼,防止沒必要的重複注釋資訊。對于實作代碼中巧妙的、晦澀的、有趣的、重要的地方加以注釋。注釋不是為了名詞解釋(what),而是說明用途(why)。

4、修改代碼時,維護代碼周邊的所有注釋,以保證注釋與代碼的一緻性,不再有用的注釋要删除

不要将無用的代碼留在注釋中,随時可以從源代碼配置庫中找回代碼;即使隻是想暫時排除代碼,也要留個标注,不然可能會忘記處理它。

5、檔案頭部應進行注釋,注釋必須列出:版權說明、版本号、生成日期、作者姓名、工号、内容、功能說明、與其它檔案的關系、修改日志等,頭檔案的注釋中還應有函數功能簡要說明

正确示例:下面這段頭檔案的頭注釋比較标準,當然,并不局限于此格式,但上述資訊建議要包含在内。

6、函數聲明處注釋描述函數功能、性能及用法,包括輸入和輸出參數、函數傳回值、可重入的要求等;定義處較長的描述函數功能和實作要點,如實作的簡要步驟、實作的理由、 設計限制等

重要的、複雜的函數,提供外部使用的接口函數應編寫詳細的注釋。

7、全局變量要有較詳細的注釋,包括對其功能、取值範圍以及存取時注意事項等的說明

/* The ErrorCode when SCCP translate */
/* Global Title failure, as follows */ /* 變量作用、含義*/
/* 0 -SUCCESS 1 -GT Table error */
/* 2 -GT error Others -no use */ /* 變量取值範圍*/
/* only function SCCPTranslate() in */
/* this modual can modify it, and other */
/* module can visit it through call */
/* the function GetGTTransErrorCode() */ /* 使用方法*/
BYTE g_GTTranErrorCode;      

8、注釋應放在其代碼上方相鄰位置或右方,不可放在下面,如放于上方則需與其上面的代碼用空行隔開,且與下方代碼縮進相同

/* active statistic task number */
#define MAX_ACT_TASK_NUMBER 1000
#define MAX_ACT_TASK_NUMBER 1000 /* active statistic task number */
可按如下形式說明枚舉/資料/聯合結構。
/* sccp interface with sccp user primitive message name */
enum SCCP_USER_PRIMITIVE
{
    N_UNITDATA_IND, /* sccp notify sccp user unit data come */
    N_NOTICE_IND, /* sccp notify user the No.7 network can not transmission this message */
    N_UNITDATA_REQ, /* sccp user's unit data transmission request*/
};      

9、對于 switch語句下的case語句,如果因為特殊情況需要處理完一個case後進入下一個case處理,必須在該case語句處理完、下一個case語句前加上明确的注釋

這樣比較清楚程式編寫者的意圖,有效防止無故遺漏break語句。

case CMD_FWD:
    ProcessFwd();
    /* now jump into c ase CMD_A */
case CMD_A:
    ProcessA();
    break;
//對于中間無處理的連續case,已能較清晰說明意圖,不強制注釋。
switch (cmd_flag)
    {
        case CMD_A:
        case CMD_B:
    {
        ProcessCMD();
        break;
    }
    ……
}      

10、避免在注釋中使用縮寫,除非是業界通用或子系統内标準化的縮寫

11、同一産品或項目組統一注釋風格

12、避免在一行代碼或表達式的中間插入注釋

除非必要,不應在代碼或表達中間插入注釋,否則容易使代碼可了解性變差

13、注釋應考慮程式易讀及外觀排版的因素,使用的語言若是中、英兼有的,建議多使用中文,除非能用非常流利準确的英文表達,對于有外籍員工的,由産品确定注釋語言

注釋語言不統一,影響程式易讀性和外觀排版,出于對維護人員的考慮,建議使用中文。

14、檔案頭、函數頭、全局常量變量、類型定義的注釋格式采用工具可識别的格式

采用工具可識别的注釋格式,例如doxygen格式,友善工具導出注釋形成幫助文檔。以doxygen格式為例,檔案頭,函數和全部變量的注釋的示例如下:

華為C語言程式設計規範(精華總結)

1、程式塊采用縮進風格編寫, 每級縮進為4個空格

2、相對獨立的程式塊之間、變量說明之後必須加空行

錯誤示例:如下例子不符合規範。

if (!valid_ni(ni))
{
    // program code
    ...
}
repssn_ind = ssn_data[index].repssn_index;
repssn_ni = ssn_data[index].ni;
正确示例:
if (!valid_ni(ni))
{
    // program code
    ...
}
repssn_ind = ssn_data[index].repssn_index;
repssn_ni = ssn_data[index].ni;      

3、一條語句不能過長,如不能拆分需要分行寫。一行到底多少字元換行比較合适,産品可以自行确定

對于目前大多數的PC來說,132比較合适(80/132是VTY常見的行寬值);對于新PC寬屏顯示器較多的産品來說,可以設定更大的值。換行時有如下建議:

換行時要增加一級縮進,使代碼可讀性更好;

低優先級操作符處劃分新行;換行時操作符應該也放下來,放在新行首;

換行時建議一個完整的語句放在一行,不要根據字元數斷行。

if ((temp_flag_var == TEST_FLAG)
&&(((temp_counter_var - TEST_COUNT_BEGIN) % TEST_COUNT_MODULE) >= TEST_COUNT_THRESHOLD))
{
    // process code
}      

4、多個短語句(包括指派語句)不允許寫在同一行内 ,即一行隻寫一條語句

int a = 5; int b= 10; //不好的排版      
int a = 5;
int b= 10;      

5、if 、 for 、 do 、 while 、 case 、 switch 、 default 等語句獨占一行

執行語句必須用縮進風格寫,屬于if、for、do、while、case、switch、default等下一個縮進級别;

一般寫if、for、do、while等語句都會有成對出現的„{}‟,對此有如下建議可以參考:if、for、do、while等語句後的執行語句建議增加成對的“{}”;如果if/else配套語句中有一個分支有“{}”,那麼另一個分支即使一行代碼也建議增加“{}”;添加“{”的位置可以在if等語句後,也可以獨立占下一行;獨立占下一行時,可以和if在一個縮進級别,也可以在下一個縮進級别;但是如果if語句很長,或者已經有換行,建議“{”使用獨占一行的寫法。

6、在兩個以上的關鍵字、變量、常量進行對等操作時,它們之間的操作符之前、之後或者前後要加空格 ; 進行非對等操作時,如果是關系密切的立即操作符(如-> > ),後不應加空格

采用這種松散方式編寫代碼的目的是使代碼更加清晰。

在已經非常清晰的語句中沒有必要再留白格,如括号内側(即左括号後面和右括号前面)不需要加空格,多重括号間不必加空格,因為在C語言中括号已經是最清晰的标志了。在長語句中,如果需要加的空格非常多,那麼應該保持整體清晰,而在局部不加空格。給操作符留白格時不要連續留兩個以上空格。

1、逗号、分号隻在後面加空格。

int a, b, c;      

2、比較操作符, 指派操作符"="、 "+=",算術操作符"+"、"%",邏輯操作符"&&"、"&",位域操作符"<<"、"^"等雙目操作符的前後加空格。

if (current_time >= MAX_TIME_VALUE)
a = b + c;
a *= 2;
a = b ^ 2;
3、"!"、"~"、"++"、"--"、"&"(位址操作符)等單目操作符前後不加空格。
*p = 'a'; // 内容操作"*"與内容之間
flag = !is_empty; // 非操作"!"與内容之間
p = &mem; // 位址操作"&" 與内容之間
i++;      

4、"->"、"."前後不加空格。

p->id = pid; // "->"指針前後不加空格

5、if、for、while、switch等與後面的括号間應加空格,使if等關鍵字更為突出、明顯。

if (a >= b && c > d)

7、注釋符(包括/**/、//)與注釋内容之間要用一個空格進行分隔

8、源程式中關系較為緊密的代碼應盡可能相鄰

1、使用編譯器的最高告警級别,了解所有的告警,通過修改代碼而不是降低告警級别來消除所有告警

編譯器是你的朋友,如果它發出某個告警,這經常說明你的代碼中存在潛在的問題。

2、在産品軟體(項目組)中,要統一編譯開關、靜态檢查選項以及相應告警清除政策

如果必須禁用某個告警,應盡可能單獨局部禁用,并且編寫一個清晰的注釋,說明為什麼屏蔽。某些語句經編譯/靜态檢查産生告警,但如果你認為它是正确的,那麼應通過某種手段去掉告警資訊。

4、本地建構工具(如 PC-Lint)的配置應該和持續內建的一緻

兩者一緻,避免經過本地建構的代碼在持續內建上建構失敗

5、 使用版本控制(配置管理)系統,及時簽入通過本地建構的代碼,確定簽入的代碼不會影響建構成功

及時簽入代碼降低內建難度。

6、要小心地使用編輯器提供的塊拷貝功能程式設計

以上為自我總結,感興趣的同志,推薦閱讀全文,也就60餘頁。

繼續閱讀