C語言編碼規範彙總篇

C語言編碼規範 1--文件與目錄


版權聲明:本文爲博主原創文章,遵循 CC 4.0 by-sa 版權協議,轉載請附上原文出處鏈接和本聲明。



1、文件及目錄的命名規定可用的字符集是[A-Z;a-z;0-9;._-]。

2、源文件名後綴用小寫字母 .c .h。

3、文件的命名要準確清晰地表達其內容,同時文件名應該精練,防止文件名過長而造成使用不便。在文件名中可以適當地使用縮寫。

以下提供兩種命名方式以供參考:

(1)各程序模塊的文件命名開頭 2 個消協字母代表本模塊的功能:

          如:主控程序爲 mpMain.c,mpDisp.c …

(2)不寫模塊功能標識:

          如:主控程序爲 Main.c,Disp.c …

 

4、一個軟件包或一個邏輯組件的所有頭文件和源文件建議放在一個單獨的目錄下,這樣有利於查找並使用相關的文件,有利於簡化一些編譯工具的設置。

5、對於整個項目需要的公共頭文件,應存放在一個單獨的目錄下(例如:myProject/include)下,可避免其他編寫人引用時目錄太過分散的問題。

6、對於源碼文件中的段落安排,我們建議按如下的順序排列: 

  • a. 文件頭註釋
  • b. 防止重複引用頭文件的設置
  • c. #include 部分
  • d. #define 部分
  • e. enum 常量聲明
  • f. 類型聲明和定義,包括 struct、union、typedef 等
  • g. 全局變量聲明
  • h. 文件級變量聲明
  • i. 全局或文件級函數聲明
  • j. 函數實現。按函數聲明的順序排列
  • k. 文件尾註釋

 

7、在引用頭文件時,不要使用絕對路徑。如果使用絕對路徑,當需要移動目錄時,必須修改所有相關代碼,繁瑣且不安全;使用相對路徑,當需要移動目錄時,只需修改編譯器的某個選項即可。

例如:

​#include “/project/inc/hello.h” /* 不應使用絕對路徑 */
#include “../inc/hello.h”       /* 可以使用相對路徑 */

8、在引用頭文件時,使用 <> 來引用預定義或者特定目錄的頭文件,使用 “” 來引用當前目錄或者路徑相對於當前目錄的頭文件。

#include <stdio.h>        /* 標準頭文件 */
#include <projdefs.h>     /* 工程指定目錄頭文件 */

#include “global.h”     /* 當前目錄頭文件 */
#include “inc/config.h” /* 路徑相對於當前目錄的頭文件 */

9、爲了防止頭文件被重複引用,應當用 ifndef/define/endif 結構產生預處理塊。

#ifndef __DISP_H   /* 文件名前名加兩個下劃線“__”,後面加 “_H”
#define __DISP_H
...
...
#endif

10、頭文件中只存放“聲明”而不存放“定義”,通過這種方式可以避免重複定義。

/* 模塊 1 頭文件: module1.h */
extern int a = 5; /* 在模塊 1 的 .h 文件中聲明變量 */
 
/* 模塊 1 實現文件:module1.c */
uint8_t g_ucPara; /* 在模塊 1 的 .h 文件中定義全局變量 g_ucPara */

11、如果其它模塊需要引用全局變量 g_ucPara, 只需要在文件開頭包含 module1.h

/* 模塊 2 實現文件:module2.c */
#include “module1.h” /* 在模塊 2 中包含模塊 1 的 .h 文件 */
......
g_ucPara = 0;
......

12、對於文件的長度沒有非常嚴格的要求,但應儘量避免文件過長。一般來說,文件長度應儘量保持在1000 行之內。


 


C語言編碼規範 2--排版


1、程序塊要採用縮進風格編寫,縮進的空格數爲 4 個。

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

void DemoFunc(void)
{
	uint8_t i;
	                                //<---- 局部變量和語句間空一行
	/* 功能塊 1 */
	for (i = 0; i < 10; i++)
	{
		//...
	}
	                                //<---- 不同的功能塊間空一行
	/* 功能塊 2 */
	for (i = 0; i < 20; i++)
	{
		//...
	}
}


3、作符處劃分新行,操作符放在新行之首,劃分出的新行要進行適當的縮進,使排版整齊,語句可讀。

if ((ucParam1 == 0) && (ucParam2 == 0) && (ucParam3 == 0)
        || (ucParam4 == 0)) < ---- 長表達式需要換行書寫
{
	//......
}


4、不允許把多個短語句寫在一行中,即一行只寫一條語句。

rect.length = 0; rect.width = 0;     //<---- 不正確的寫法
 
rect.length = 0;                     //<---- 正確的寫法
rect.width  = 0;


5、對齊使用 TAB 鍵,1 個 TAB 對應 4 個字符位。

6、函數或過程的開始、結構的定義及循環、判斷等語句中的代碼都要採用縮進風格,case 語句下的情況處理語句也要遵從語句縮進要求。

7、程序塊的分界符(如大括號‘{’和‘}’ )應各獨佔一行並且位於同一列,同時與引用它們的語句左對齊。在函數體的開始、類的定義、結構的定義、枚舉的定義以及 if、for、do、while、switch、case 語句中的程序都要採用如上的縮進方式。對於與規則不一致的現存代碼,應優先保證同一模塊中的風格一致性。

for (...) {                           <---- 不規範的寫法
    //... /* program code */
}
 
for (...)
{                                     <---- 規範的寫法
    //... /* program code */
}
 
if (...){                             <---- 不規範的寫法
    //... /* program code */
}
 
if (...)
{                                     <---- 規範的寫法
    //... /* program code */
}


8、在兩個以上的關鍵字、變量、常量進行對等操作時,它們之間的操作符之前、之後或者前後要加空格;進行非對等操作時,如果是關係密切的立即操作符(如->),後不應加空格。
說明:採用這種鬆散方式編寫代碼的目的是使代碼更加清晰。
由於留空格所產生的清晰性是相對的,所以,在已經非常清晰的語句中沒有必要再留空格,如果語句已足夠清晰則括號內側(即左括號後面和右括號前面)不需要加空格,多重括號間不必加空格,因爲在 C語言中括號已經是最清晰的標誌了。
在長語句中,如果需要加的空格非常多,那麼應該保持整體清晰,而在局部不加空格。給操作符留空格時不要連續留兩個以上空格。

示例:
(1)逗號、分號只在後面加空格。

int_32 a, b, c;


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

if (current_time >= MAX_TIME_VALUE)
a = b + c;
a *= 2;
a = b ^ 2;


(3)"!"、"~"、"++"、"--"、"&"(地址運算符)等單目操作符前後不加空格。

*p = 'a';                 /* 內容操作"*"與內容之間 */
flag = !isEmpty;          /* 非操作"!"與內容之間 */
p = &mem;                 /* 地址操作"&" 與內容之間 */
i++;                      /* "++","--"與內容之間 */


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

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


(5)if、for、while、switch 等與後面的括號間應加空格,使 if 等關鍵字更爲突出、明顯,函數名與其後的括號之間不加空格,以與保留字區別開。

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

 


C語言編碼規範 3 --註釋


(1) 一般情況下,源程序有效註釋量必須在 20%以上。

說明:註釋的原則是有助於對程序的閱讀理解,在該加的地方都加,註釋不宜太多也不能太少,註釋語言必須準確、易懂、簡潔。

(2) 在文件的開始部分,應該給出關於文件版權、內容簡介、修改歷史等項目的說明。
具體的格式請參見如下的說明。在創建代碼和每次更新代碼時,都必須在文件的歷史記錄中標註版本號、日期、作者、更改說明等項目。其中的版本號的格式爲兩個數字字符和一個英文字母字符。數字字符表示大的改變,英文字符表示小的修改。如果有必要,還應該對其它的註釋內容也進行同步的更改。注意:註釋第一行星號要求爲 76 個,結尾行星號爲 1 個。

/****************************************************************************
* Copyright (C), 2010-2011,xxxx-xxxx有限責任公司
* 文件名: main.c
* 內容簡述:
*
* 文件歷史:
* 版本號     日期         作者       說明
* 01a       2010-07-29   RootCode   創建該文件
* 01b       2010-08-20   RootCode   改爲可以在字符串中發送回車符
* 02a       2010-12-03   RootCode   增加文件頭註釋
*/

(3) 對於函數,在函數實現之前,應該給出和函數的實現相關的足夠而精練的註釋信息。內容包括本函數功能介紹,調用的變量、常量說明,形參說明,特別是全局、全程或靜態變量(慎用靜態變量),要求對其初值,調用後的預期值作詳細的闡述。具體的書寫格式和包含的各項內容請參見如下的例子。
示例:
下面這段函數的註釋比較標準,當然,並不侷限於此格式,但上述信息建議要包含在內。

/****************************************************************************
* 函數名   : SendToCard()
* 功  能   : 向讀卡器發命令,如果讀卡器進入休眠,則首先喚醒它
* 輸 入    : 全局變量 gaTxCard[]存放待發的數據
* 全局變量 : gbTxCardLen 存放長度
* 輸    出 : 無
*/

(4) 邊寫代碼邊註釋,修改代碼同時修改相應的註釋,以保證註釋與代碼的一致性。不再有用的註釋要刪除。
(5) 註釋的內容要清楚、明瞭,含義準確,防止註釋二義性。
說明:錯誤的註釋不但無益反而有害。註釋主要闡述代碼做了什麼(What),或者如果有必要的話,闡述爲什麼要這麼做(Why),註釋並不是用來闡述它究竟是如何實現算法(How)的。

(6) 避免在註釋中使用縮寫,特別是非常用縮寫。
說明:在使用縮寫時或之前,應對縮寫進行必要的說明。

(7) 註釋應與其描述的代碼靠近,對代碼的註釋應放在其上方或右方(對單條語句的註釋)相鄰位置,不可放在下面,如放於上方則需與其上面的代碼用空行隔開。
示例:如下例子不符合規範。
例 1:不規範的寫法

/* 獲取複本子系統索引和網絡指示器 */
                                            <---- 不規範的寫法,此處不應該空行
repssn_ind = ssn_data[index].repssn_index;
repssn_ni = ssn_data[index].ni;


例 2:不規範的寫法

repssn_ind = ssn_data[index].repssn_index;
repssn_ni = ssn_data[index].ni;
/* 獲取複本子系統索引和網絡指示器 */             <---- 不規範的寫法,應該在語句前註釋


例 3:規範的寫法

/* 獲取複本子系統索引和網絡指示器 */
repssn_ind = ssn_data[index].repssn_index;
repssn_ni = ssn_data[index].ni;


例 4:不規範的寫法,顯得代碼過於緊湊

/* code one comments */
program code one
/* code two comments */             <---- 不規範的寫法,兩段代碼之間需要加空行
program code two


例 5:規範的寫法

/* code one comments */
program code one
/* code two comments */
program code two

(8) 註釋與所描述內容進行同樣的縮排。
說明:可使程序排版整齊,並方便註釋的閱讀與理解。
例 1:如下例子,排版不整齊,閱讀稍感不方便。

void example_fun( void )
{
/* code one comments */ <---- 不規範的寫法,註釋和代碼應該相同的縮進
    CodeBlock One
/* code two comments */ <---- 不規範的寫法,註釋和代碼應該相同的縮進
    CodeBlock Two
}


例 2:正確的佈局。

void example_fun( void )
{
    /* code one comments */
    CodeBlock One
    /* code two comments */
    CodeBlock Two
}


(9) 對變量的定義和分支語句(條件分支、循環語句等)必須編寫註釋。
說明:這些語句往往是程序實現某一特定功能的關鍵,對於維護人員來說,良好的註釋幫助更好的理解程序,有時甚至優於看設計文檔。

(10) 對於 switch 語句下的 case 語句,如果因爲特殊情況需要處理完一個 case 後進入下一個 case 處理,必須在該 case 語句處理完、下一個 case 語句前加上明確的註釋。
說明:這樣比較清楚程序編寫者的意圖,有效防止無故遺漏 break 語句。
示例(注意斜體加粗部分):

(11) 註釋格式儘量統一,建議使用“/* …… */”,因爲 C++註釋“//”並不被所有 C 編譯器支持。

(12) 註釋應考慮程序易讀及外觀排版的因素,使用的語言若是中、英兼有的,建議多使用中文,除非能非常流利準確的用英文表達。
說明:註釋語言不統一,影響程序易讀性和外觀排版,出於對維護人員的考慮,建議使用中文。

標識符命名
(13) 標識符的命名要清晰、明瞭,有明確含義,同時使用完整的單詞或大家基本可以理解的縮寫,避免使人產生誤解。
說明:較短的單詞可通過去掉“元音”形成縮寫;較長的單詞可取單詞的頭幾個字母形成縮寫;一些單詞有大家公認的縮寫。
示例:如下單詞的縮寫能夠被大家基本認可。

        temp 可縮寫爲 tmp;
        flag 可縮寫爲 flg;
        statistic 可縮寫爲 stat;
        increment 可縮寫爲 inc;
        message 可縮寫爲 msg;



(14) 命名中若使用特殊約定或縮寫,則要有註釋說明。
說明:應該在源文件的開始之處,對文件中所使用的縮寫或約定,特別是特殊的縮寫,進行必要的註釋說明。

(15) 自己特有的命名風格,要自始至終保持一致,不可來回變化。
說明:個人的命名風格,在符合所在項目組或產品組的命名規則的前提下,纔可使用。(即命名規則中沒有規定到的地方纔可有個人命名風格)。

(16) 對於變量命名,禁止取單個字符(如 i、j、k...),建議除了要有具體含義外,還能表明其變量類型、數據類型等,但 i、j、k 作局部循環變量是允許的。
說明:變量,尤其是局部變量,如果用單個字符表示,很容易敲錯(如i寫成j),而編譯時又檢查不出來,有可能爲了這個小小的錯誤而花費大量的查錯時間。

(17) 命名規範必須與所使用的系統風格保持一致,並在同一項目中統一,比如採用 UNIX 的全小寫加下劃線的風格或大小寫混排的方式,不要使用大小寫與下劃線混排的方式,用作特殊標識如標識成員變量或全局變量的 m_和 g_,其後加上大小寫混排的方式是允許的。
示例:

Add_User                         //不允許,
add_user、AddUser、m_AddUser      //允許。

(18) 除非必要,不要用數字或較奇怪的字符來定義標識符。
示例:如下命名,使人產生疑惑。

uint8_t dat01;
void Set00(uint_8 c);

應改爲有意義的單詞命名。

uint8_t ucWidth;
void SetParam(uint_8 _ucValue);

(19) 在同一軟件產品內,應規劃好接口部分標識符(變量、結構、函數及常量)的命名,防止編譯、鏈接時產生衝突。
說明:對接口部分的標識符應該有更嚴格限制,防止衝突。如可規定接口部分的變量與常量之前加上“模塊”標識等。

(20) 除了編譯開關/頭文件等特殊應用,應避免使用_EXAMPLE_TEST_之類以下劃線開始和結尾的定義。

 


C語言編碼規範 4 --可讀性


(1) 注意運算符的優先級,並用括號明確表達式的操作順序,避免使用默認優先級。

說明:防止閱讀程序時產生誤解,防止因默認的優先級與設計思想不符而導致程序出錯。
示例:下列語句中的表達式
正確的寫法:

word = (high << 8) | low;
if ((a | b) && (a & c))
if ((a | b) < (c & d))

錯誤的寫法:

word = high << 8 | low;
if (a | b && a & c)
if (a | b < c & d)         /* 造成了判斷條件出錯 */

(2) 避免使用不易理解的數字,用有意義的標識來替代。涉及物理狀態或者含有物理意義的常量,不應直接使用數字,必須用有意義的枚舉或宏來代替。

示例:如下的程序可讀性差。

if (Trunk[index].trunk_state == 0)     //<---- 不規範的寫法,應使用有意義的標識
{
    Trunk[index].trunk_state = 1;      //<---- 不規範的寫法,應使用有意義的標識
    //...                              /* program code */
}

應改爲如下形式。

enum trunk_state_e
{
    TRUNK_IDLE = 0,
    TRUNK_BUSY = 1
};
 
if (Trunk[index].trunk_state == TRUNK_IDLE)
{
    Trunk[index].trunk_state = TRUNK_BUSY;
    //...                                 /* program code */
}

(3) 不要使用難懂的技巧性很高的語句,除非很有必要時。
說明:高技巧語句不等於高效率的程序,實際上程序的效率關鍵在於算法。
示例:如下表達式,考慮不周就可能出問題,也較難理解。

* stat_poi ++ += 1;
* ++ stat_poi += 1;
 
 
// 應分別改爲如下
*stat_poi += 1;
stat_poi++;                 /* 此二語句功能相當於“ * stat_poi ++ += 1; ”*/
 
++ stat_poi;
*stat_poi += 1;             /* 此二語句功能相當於“ * ++ stat_poi += 1; ”*/


 


C語言編碼規範 5--變量、結構、常量、宏


 

1、爲了方便書寫及記憶,變量類型採用如下重定義:

typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned long int uint32_t;
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed long int int32_t;
 
#define __IO volatile


2、常見類型的前綴
(1)對於一些常見類型的變量,應在其名字前標註表示其類型的前綴。前綴用小寫字母表示。前綴的使用請參照下列表格中說明。

(2)對於幾種變量類型組合,前綴可以迭加。

3、變量作用域的前綴
爲了清晰的標識變量的作用域,減少發生命名衝突,應該在變量類型前綴之前再加上表示變量作用域的前綴,並在變量類型前綴和變量作用域前綴之間用下劃線‘-’隔開。
具體的規則如下:
(1)對於全局變量(global variable),在其名稱前加“g”和變量類型符號前綴。

uint32_t g_ulParaWord;
uint8_t g_ucByte;

(2)對於靜態變量(static variable),在其名稱前加“s”和變量類型符號前綴。

static uint32_t s_ulParaWord;
static uint8_t s_ucByte;

(3)函數內部等局部變量前不加作用域前綴。
(4)對於常量,當可能發生作用域和名字衝突問題時,以上幾條規則對於常量同樣適用。注意,雖然常量名的核心部分全部大寫,但此時常量的前綴仍然用小寫字母,以保持前綴的一致性。

4、對於結構體命名類型,表示類型的名字,所有名字以小寫字母“tag”開頭,之後每個英文單詞的第一個字母大寫(包括第一個單詞的第一個字母),其他字母小寫,結尾_T 標識。單詞之間不使用下劃線分隔,結構體變量以 t 開頭。

/* 結構體命名類型名 */
typedef struct tagBillQuery_T
{
    //...
}BillQuery_T;
 
/* 結構體變量定義 */
BillQuery_T tBillQuery;
 
// 對於枚舉定義全部採用大寫,結尾_E 標識。
typedef enum
{
    KB_F1 = 0,     /* F1 鍵代碼 */
    KB_F2,         /* F2 鍵代碼 */
    KB_F3          /* F3 鍵代碼 */
}KEY_CODE_E;

5、常量、宏、模版的名字應該全部大寫。如果這些名字由多個單詞組成,則單詞之間用下劃線分隔。
宏指所有用宏形式定義的名字,包括常量類和函數類;常量也包括枚舉中的常量成員。

#define LOG_BUF_SIZE 8000


6、不推薦使用位域。

 


C語言編碼規範 6--函數


1、函數的命名規則。每一個函數名前綴需包含模塊名,模塊名爲小寫,與函數名區別開。
如:uartReceive(串口接收)
備註:對於非常簡單的程序,可以不加模塊名。


2、函數的的形參需另啓一行,在後面給予說明,形參都以下劃線_開頭,已示與普通變量進行區分,對於沒有形參爲空的函數(void)括號緊跟函數後面。

/******************************************************************************
* 函數名:uartConvUartBaud
* 功  能:波特率轉換
* 輸  入:_ulBaud : 波特率
* 輸  出:無
* 返  回:uint32- 轉換後的波特率值
*/
uint32_t uartConvUartBaud(uint32_t _ulBaud)
{
    uint32_t ulBaud;
    ulBaud = ulBaud * 2;         /* 計算波特率 */
    // ......
    return ulBaud;
}

3、一個函數僅完成一件功能。

4、函數名應準確描述函數的功能。避免使用無意義或含義不清的動詞爲函數命名。使用動賓詞組爲執行某操作的函數命名。
說明:避免用含義不清的動詞如process、handle等爲函數命名,因爲這些動詞並沒有說明要具體做什麼。
示例:參照如下方式命名函數。

void PrintRecord(uint32_t _RecInd);
int32 InputRecord(void);
uint8_t GetCurrentColor(void);

5、檢查函數所有參數輸入的有效性。
說明:如果約定由調用方檢查參數輸入,則應使用assert()之類的宏,來驗證所有參數輸入的有效性。

6、檢查函數所有非參數輸入的有效性,如數據文件、公共變量等。
說明:函數的輸入主要有兩種:一種是參數輸入;另一種是全局變量、數據文件的輸入,即非參數輸入。函數在使用輸入之前,應進行必要的檢查。

7、防止將函數的參數作爲工作變量。
說明:將函數的參數作爲工作變量,有可能錯誤地改變參數內容,所以很危險。對必須改變的參數,最好先用局部變量代之,最後再將該局部變量的內容賦給該參數。

8、避免設計五個以上參數函數,不使用的參數從接口中去掉。
說明:目的減少函數間接口的複雜度,複雜的參數可以使用結構傳遞。

9、在調用函數填寫參數時,應儘量減少沒有必要的默認數據類型轉換或強制數據類型轉換。
說明:因爲數據類型轉換或多或少存在危險。

10、避免使用 BOOL 參數。
說明:原因有二,其一是BOOL參數值無意義,TURE/FALSE的含義是非常模糊的,在調用時很難知道該參數到底傳達的是什麼意思;其二是BOOL參數值不利於擴充。還有NULL也是一個無意義的單詞。

11、函數的返回值要清楚、明瞭。除非必要,最好不要把與函數返回值類型不同的變量,以編譯系統默認的轉換方式或強制的轉換方式作爲返回值返回。

12、防止把沒有關聯的語句放到一個函數中。
說明:防止函數或過程內出現隨機內聚。隨機內聚是指將沒有關聯或關聯很弱的語句放到同一個函數或過程中。隨機內聚給函數或過程的維護、測試及以後的升級等造成了不便,同時也使函數或過程的功能不明確。使用隨機內聚函數,常常容易出現在一種應用場合需要改進此函數,而另一種應用場合又不允許這種改進,從而陷入困境。
      在編程時,經常遇到在不同函數中使用相同的代碼,許多開發人員都願把這些代碼提出來,並構成一個新函數。若這些代碼關聯較大並且是完成一個功能的,那麼這種構造是合理的,否則這種構造將產生隨機內聚的函數。
示例:如下函數就是一種隨機內聚。

矩形的長、寬與點的座標基本沒有任何關係,故以上函數是隨機內聚。
應如下分爲兩個函數:

13、減少函數本身或函數間的遞歸調用。
說明:遞歸調用特別是函數間的遞歸調用(如A->B->C->A),影響程序的可理解性;遞歸調用一般都佔用較多的系統資源(如棧空間);遞歸調用對程序的測試有一定影響。故除非爲某些算法或功能的實現方便,應減少沒必要的遞歸調用。


14、改進模塊中函數的結構,降低函數間的耦合度,並提高函數的獨立性以及代碼可讀性、效率和可維護性。優化函數結構時,要遵守以下原則:
(1)能影響模塊功能的實現。
(2)仔細考查模塊或函數出錯處理及模塊的性能要求並進行完善。
(3)通過分解或合併函數來改進軟件結構。
(4)考查函數的規模,過大的要進行分解。
(5)降低函數間接口的複雜度。
(6)不同層次的函數調用要有較合理的扇入、扇出。
(7)函數功能應可預測。
(8)提高函數內聚。(單一功能的函數內聚最高)
說明:對初步劃分後的函數結構應進行改進、優化,使之更爲合理。

 

 

 

 

 





 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章