嵌入式C語言代碼規範

C語言代碼規範

參考安富萊C語言編碼規範

1.文件與目錄

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

1 #include <file.h>
執行這條指令時,它會在系統目錄中去查找 file.h 文件。在此條碼命令中,不會去當前路徑和附加路徑中查找文件。

2 #include "file.h"
執行這條指令時,它首先會搜索附加路徑,如果沒有則會搜索系統路徑,如果還沒有則會去搜索當前路徑。

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

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

9、頭文件中只存放“聲明”而不存放“定義”,通過這種方式可以避免重複定義。如果其它模塊需要引用全局變量 g_temp, 只需要在文件開頭包含 disp.h

#ifndef __DISP_H /* 文件名前名加兩個下劃線“__”,後面加 “_H”
#define __DISP_H
/** 全局變量聲明 */
extern uint32_t g_temp;
#endif /* disp.h*/
#include "disp.h"
/** 全局變量定義 */
uint32_t g_temp = 0;

2.排版

1、程序塊要採用縮進風格編寫,縮進的空格數爲 4 個。儘量用空格,在不同環境下Tab鍵代表的空格可能不同,導致排版混亂。
在這裏插入圖片描述
在這裏插入圖片描述

2、相對獨立的程序塊之間、變量說明之後必須加空行增加可讀性、變量最好在使用時在定義。

3、當一行太長時,可以按操作符處劃分新行,劃分出的新行要進行適當的縮進,使排版整齊,語句可讀。

ANO_DT_send_int16((short)(sin(data1/180.0f * PI) * 100),
                  (short)(sin(data2/180.0f * PI) * 100),
                  (short)(sin(data3/180.0f * PI) * 100),
                  (short)(sin(data4/180.0f * PI) * 100), 
                  (short)(sin(data5/180.0f * PI) * 100), 
                   0, 
                   0,
                   0);  

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

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

for (...) { <---- 不規範的寫法

... /* program code */

}

for (...)

{ <---- 規範的寫法

... /* program code */

}

if (...)

{ <---- 不規範的寫法

... /* program code */

}

if (...)

{ <---- 規範的寫法

... /* program code */

}

6、在兩個以上的關鍵字、變量、常量進行對等操作時,它們之間的操作符之前、之後或者前後要加空格;進行非對等操作時,如果是關係密切的立即操作符(如->),後不應加空格。
說明:採用這種鬆散方式編寫代碼的目的是使代碼更加清晰。
由於留空格所產生的清晰性是相對的,所以,在已經非常清晰的語句中沒有必要再留空格,如果語句已足夠清晰則括號內側(即左括號後面和右括號前面)不需要加空格,多重括號間不必加空格,因爲在 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; /* "->"指針前後不加空格 */

3.註釋

1、一般情況下,源程序有效註釋量必須在 20%以上。
說明:註釋的原則是有助於對程序的閱讀理解,在該加的地方都加,註釋不宜太多也不能太少,註釋語言必須準確、易懂、簡潔。

2、在文件的開始部分,應該給出關於文件版權、內容簡介、修改歷史等項目的說明。
具體的格式請參見如下的說明。在創建代碼和每次更新代碼時,都必須在文件的歷史記錄中標註版本號、日期、作者、更改說明等項目。

/*!
  * @file     LQ_ADC.c
  *
  * @brief    ADC驅動文件
  *
  * @company  北京龍邱智能科技
  *
  * @author   LQ-005
  *
  * @note     無
  *
  * @version  V1.1  2019/12/06 優化註釋 Doxygen
  *
  * @par URL  http://shop36265907.taobao.com
  *           http://www.lqist.cn
  *
  * @date     2019/10/18 星期五
  */ 

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

/*!
 * @brief    ADC通道初始化
 *
 * @param    channel   :  ADC通道 LQ_ADC.h中的一個枚舉體  
 * @param    bit       :  ADC通道精度 LQ_ADC.h中的一個枚舉體  
 *
 * @return   無
 *
 * @note     讀取ADC之前一定要調用該函數對ADC通道進行初始化
 *
 * @see      ADC_InitConfig(ADC0CH0_P0_10, ADC_12bit);  //初始化ADC通道0 P0_10
 *
 * @date     2019/10/21 星期一
 */
void ADC_InitConfig(ADCn_Ch channel, ADC_nbit bit)

對於宏定

/*! CTIMER 最大佔空比 可自行修改 */
#define  CMTER_PWM_MAX    10000

結構體、枚舉體註釋

/** 
  * @brief CTIMER模塊 脈衝計數通道
  * @note  CTIMER 模塊 脈衝捕獲通道 
  * @note  CTIMER 模塊的輸入管腳並不是直接CTIMER連接的  而是通過INPUTMUX模塊連接的
  * @note  CTIMER計數器 ---  INPUTMUX輸入多路複用模塊17路通道  ----  芯片外部管腳
  * @note  簡單說 就是CTIMER的捕獲通道每個都可以與 INPUTMUX模塊的17路通道相鏈接
  * @note  INPUTMUX模塊的17路通道相鏈接的管腳如下
  */ 
typedef enum
{
    CTInput0_P0_1  = 0x0000 + 1,  CTInput0_P0_13 = 0x0000 + 2,     /*!< INPUTMUX—CTIMER 輸入通道0管腳  */  
    CTInput1_P0_14 = 0x0100 + 1,  CTInput1_P0_2  = 0x0100 + 2,     /*!< INPUTMUX—CTIMER 輸入通道1管腳  */  
    CTInput2_P1_0  = 0x0200 + 1,  CTInput2_P1_28 = 0x0200 + 2,     /*!< INPUTMUX—CTIMER 輸入通道2管腳  */  
    CTInput3_P1_1  = 0x0300 + 1,  CTInput3_P1_26 = 0x0300 + 2,     /*!< INPUTMUX—CTIMER 輸入通道3管腳  */  
    CTInput4_P1_9  = 0x0400 + 1,  CTInput4_P0_16 = 0x0400 + 2,     /*!< INPUTMUX—CTIMER 輸入通道4管腳  */  
    CTInput5_P1_11 = 0x0500 + 1,                                   /*!< INPUTMUX—CTIMER 輸入通道5管腳  */  
    CTInput6_P1_13 = 0x0600 + 1,                                   /*!< INPUTMUX—CTIMER 輸入通道6管腳  */  
    CTInput7_P1_15 = 0x0700 + 1,                                   /*!< INPUTMUX—CTIMER 輸入通道7管腳  */  
    CTInput8_P0_24 = 0x0800 + 1,                                   /*!< INPUTMUX—CTIMER 輸入通道8管腳  */  
    CTInput9_P0_25 = 0x0900 + 1,                                   /*!< INPUTMUX—CTIMER 輸入通道9管腳  */  
    CTInput10_P0_10= 0x0A00 + 1,                                   /*!< INPUTMUX—CTIMER 輸入通道10管腳  */  
    CTInput11_P0_28= 0x0B00 + 1,                                   /*!< INPUTMUX—CTIMER 輸入通道11管腳  */  
    CTInput12_P0_4 = 0x0C00 + 1,                                   /*!< INPUTMUX—CTIMER 輸入通道12管腳  */  
    CTInput13_P0_6 = 0x0D00 + 1,                                   /*!< INPUTMUX—CTIMER 輸入通道13管腳  */  
    CTInput14_P1_20= 0x0E00 + 1,  CTInput14_P0_26= 0x0E00 + 2,     /*!< INPUTMUX—CTIMER 輸入通道14管腳  */  
    CTInput15_P0_20= 0x0F00 + 1,  CTInput15_P0_22= 0x0F00 + 2,     /*!< INPUTMUX—CTIMER 輸入通道15管腳  */  
    CTInput16_P0_15= 0x1000 + 1,                                   /*!< INPUTMUX—CTIMER 輸入通道16管腳  */  
    
} CTIMER_InputChannel_t;

全局變量

/** DMA link傳輸描述符 */
extern dma_descriptor_t s_dma_descriptor_table0[];

註釋應嚴格按以上格式進行註釋,方便日後使用Doxygen生成幫助API文檔
在這裏插入圖片描述

4、邊寫代碼邊註釋,修改代碼同時修改相應的註釋,以保證註釋與代碼的一致性。不再有用的註釋要刪除。

5、註釋的內容要清楚、明瞭,含義準確,防止註釋二義性。
說明:錯誤的註釋不但無益反而有害。註釋主要闡述代碼做了什麼(What),或者如果有必要的話,闡述爲什麼要這麼做(Why),註釋並不是用來闡述它究竟是如何實現算法(How)的。

6、普通註釋格式儘量統一,建議使用“/* …… */”註釋在代碼上方, C++註釋“//”並不被所有 C 編譯器支持。

7、註釋應考慮程序易讀及外觀排版的因素,使用的語言若是中、英兼有的,建議多使用中文,除非能非常流利準確的用英文表達。

8、標識符的命名要清晰、明瞭,有明確含義,同時使用完整的單詞或大家基本可以理解的縮寫,避免使人產生誤解。

9、命名中若使用特殊約定或縮寫,則要有註釋說明。

10、自己特有的命名風格,要自始至終保持一致,不可來回變化。

11、 對於變量命名,禁止取單個字符(如 i、j、k…),建議除了要有具體含義外,還能表明其變量類型、數據類型等,但 i、j、k 作局部循環變量是允許的。

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

13、除非必要,不要用數字或較奇怪的字符來定義標識符。

4.可讀性

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

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、不要使用難懂的技巧性很高的語句,除非證明改語句是性能瓶頸。

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

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

5、對於一些常見類型的變量,應在其名字前標註表示其類型的前綴。前綴用小寫字母表示。前綴的使用請參照下列表格中說明。
在這裏插入圖片描述

6、變量作用域的前綴
爲了清晰的標識變量的作用域,減少發生命名衝突,應該在變量類型前綴之前再加上表示變量作用域的前綴,並在變量類型前綴和變量作用域前綴之間用下劃線‘-’隔開。
具體的規則如下:
(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)對於常量,當可能發生作用域和名字衝突問題時,以上幾條規則對於常量同樣適用。注意,雖然常量名的核心部分全部大寫,但此時常量的前綴仍然用小寫字母,以保持前綴的一致性。

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