經(jīng)常都會(huì)都會(huì)有讀者問如下類似的問題:其實(shí),我們身邊就有很多“好的資源”值得學(xué)習(xí),,比如本文分享的 阿里 AliOS 的編碼風(fēng)格,。
1. 前言本文是AliOS Things提供的一套C語言代碼規(guī)范,適用的對象為符合C99標(biāo)準(zhǔn)的C語言工程,。 2. 命名本節(jié)內(nèi)容均為建議,,不作強(qiáng)制要求。 2.1. 總則各種命名均使用英文單詞及其縮寫,,非特殊情況不能使用漢語拼音或其他語言,。 2.2. 文件命名文件名全部使用小寫字母,用_ 連接,。源文件使用.c 后綴,。頭文件使用.h 后綴。 2.3. 類型命名2.3.1. 簡單類型命名 使用typedef 自定義的簡單類型命名全部使用小寫字母,,用_ 連接,,以_t 結(jié)尾。例如: typedef int32_t aos_status_t;
2.3.2. 結(jié)構(gòu)體和聯(lián)合體命名 結(jié)構(gòu)體和聯(lián)合體類型命名全部使用小寫字母,,用_ 連接,。建議使用typedef 定義一個(gè)整體的名字,以_t 結(jié)尾,。例如: typedef struct aos_list_node { struct aos_list_node *prev; struct aos_list_node *next; } aos_list_node_t;
static aos_list_node_t list_node;
2.3.3. 枚舉命名 枚舉類型命名全部使用小寫字母,,用_ 連接。建議使用typedef 定義一個(gè)整體的名字,,以_t 結(jié)尾,。枚舉值命名全部使用大寫字母,用_ 連接,,包含表示類型的前綴,。例如: typedef enum aos_socket_stage { AOS_SOCK_STG_DISCONNECTED, AOS_SOCK_STG_CONNECTED, } aos_socket_stage_t;
static aos_socket_stage_t sock_stage = AOS_SOCK_STG_DISCONNECTED;
2.4. 變量命名變量命名全部使用小寫字母,用_ 連接,。數(shù)組名稱盡量使用復(fù)數(shù)名詞,。例如: cfg_file_t cfg_files[NUM_CFG_FILES];
表示數(shù)目的變量名稱使用num(number的縮寫)加復(fù)數(shù)名詞,。例如: 表示序號(hào)的變量名稱使用單數(shù)名詞加num或index或idx(index的縮寫)。例如: unsigned int file_num; unsigned int file_index;
2.5. 函數(shù)命名函數(shù)命名全部使用小寫字母,,用_ 連接,。 2.6. 宏命名一般的宏命名全部使用大寫字母,用_ 連接,。例如: #define AOS_STRING_MAX_LEN 127
模擬函數(shù)使用方式的宏的命名規(guī)則與函數(shù)相同,。例如: #define aos_dev_set_id(dev, x) \ do { \ (dev)->id = (x); \ } while (0)
2.7. 前綴為防止命名空間污染,,公用組件中的非static函數(shù),、非static全局變量、全局類型,、全局宏的命名應(yīng)帶有前綴,。例如(假設(shè)前綴為aos ): void aos_cfg_file_close(int fd); extern char **aos_process_argv; typedef struct aos_list_node aos_list_node_t; #define AOS_STRING_MAX_LEN 127
3. 格式3.1. 文本格式源文件、頭文件,、Makefile等文本文件一律采用UTF-8 without BOM編碼,,采用Unix風(fēng)格換行格式。文本文件末尾應(yīng)有且只有一個(gè)換行符,,即末尾應(yīng)有且只有一個(gè)空行,。 3.2. 行長度每行字符數(shù)原則上不超過120。包含長路徑的#include 語句,、頭文件#define 保護(hù)可以無視此規(guī)則,。 3.2.1. 表達(dá)式換行 較長的表達(dá)式可在運(yùn)算符處換行,換行處的運(yùn)算符屬于舊行,,新行對齊到舊行中的相同邏輯層級(jí),。例如: void foo(void) { if ((aos_list_next(list_node) != &list_head && !priv) || !(strcmp(symbol, default_symbol) && blahblahblahblahblahblah() && meomeomeomeomeomeomeomeomeomeomeomeomeomeomeomeo(NULL))) { /* ... */ } }
3.2.2. 函數(shù)換行 較長的函數(shù)定義、聲明可在返回值類型和函數(shù)名稱之間換行,。若返回值為指針類型,,* 屬于新行。例如: static unsigned long blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah(void); static const manager_priv_t *blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah(int index);
較長的函數(shù)定義,、聲明,、調(diào)用可在參數(shù)列表中間換行,參數(shù)列表中間換行后新行應(yīng)縮進(jìn)至舊行第一個(gè)參數(shù)處,。例如: void blahblahblahblahblahblahblahblahblah(manager_priv_t *priv, int index, const char *proc_name);
void foo(void) { blahblahblahblahblahblahblahblahblahblah(get_manager_priv(manager), 0, 'meomeomeomeomeomeomeomeo'); }
3.2.3. 字符串換行 較長的字符串可在空格處換行,,一般情況下?lián)Q行處的空格屬于舊行。例如: void foo(void) { printf('The GNU operating system consists of GNU packages ' '(programs specifically released by the GNU Project) ' 'as well as free software released by third parties.\n'); }
3.3. 縮進(jìn)使用空格縮進(jìn),,每次4個(gè)空格,。全文不應(yīng)出現(xiàn)制表符(tab)。例如: void foo(unsigned int nbr_processes) { unsigned int i;
while (i < nbr_processes) { const char *name; /* ... */ } }
宏定義,、行尾注釋,、結(jié)構(gòu)體,、聯(lián)合體、枚舉等內(nèi)部可縮進(jìn)實(shí)現(xiàn)多行對齊,,但不作強(qiáng)制要求,。若有縮進(jìn),應(yīng)對齊到4的整數(shù)倍,。例如: /* 此處數(shù)字0縮進(jìn)32個(gè)字符,,即位于第33列。 */ #define STAGE_UPDATE_CONTINUE 0 #define STAGE_UPDATE_COMPLETE 1
/* 合法 */ #define EVENT_RX_FULL (1U << 0) #define EVENT_TX_EMPTY (1U << 1)
typdef enum socket_stage { /* 此處等號(hào)縮進(jìn)32個(gè)字符,,即位于第33列,。 */ SOCK_STG_DISCONNECTED = 0, SOCK_STG_CONNECTED = 1, } socket_stage_t;
/* 此處反斜杠縮進(jìn)40個(gè)字符,即位于第41列,。 */ #define aos_dev_set_flags(dev, x) \ do { \ (dev)->flags = (x); \ } while (0)
/* 合法 */ #define aos_dev_set_ops(dev, x) \ do { \ (dev)->ops = (x); \ } while (0)
/* 此處注釋縮進(jìn)24個(gè)字符,,即位于第25列。 */ foo(NULL); /* abc */ blahblahblahblahblah(); /* xyz */
/* 合法 */ foofoofoo(); /* abc */ foofoo(); /* xyz */
分行定義的宏,,第二行起應(yīng)縮進(jìn)一次,。例如: #define aos_dev_set_id(dev, x) \ do { \ (dev)->id = (x); \ } while (0)
switch 塊中的case 語句和default 語句與switch 語句縮進(jìn)層級(jí)相同。例如:
switch (stage) { case SOCK_STG_DISCONNECTED: foo(); break; case SOCK_STG_CONNECTED: sock->connected = 1; break; default: break; }
3.4. 花括號(hào)函數(shù)體的左花括號(hào)另起一行,;其他情況下左花括號(hào)不另起一行,。一般情況下左花括號(hào)后續(xù)內(nèi)容另起一行;宏定義中,、數(shù)組,、結(jié)構(gòu)體、聯(lián)合體初始化時(shí)若花括號(hào)中內(nèi)容較短則左花括號(hào)后續(xù)內(nèi)容可以不另起一行,。一般情況下右花括號(hào)另起一行,;宏定義中、數(shù)組,、結(jié)構(gòu)體,、聯(lián)合體初始化時(shí)若花括號(hào)中內(nèi)容較短則右花括號(hào)可以不另起一行。右花括號(hào)與后續(xù)內(nèi)容組合成一行,。例如: typedef struct manager_priv { int index; void *data; } manager_priv_t;
#define set_manager_index(x, idx) do { (x)->priv->index = (idx); } while (0)
#ifdef __cplusplus extern 'C' { #endif
void foo(void) { int i = 0;
/* ... */
if (i == 0) { /* ... */ } else { /* ... */ } }
manager_priv_t priv = { 0, NULL, };
#ifdef __cplusplus } #endif
3.5. 空格行尾不應(yīng)有空格,。三元操作符和二元操作符(獲取成員的. 和-> 操作符除外)前后留有空格。例如: x = a ? b : c; v = w * x + y / z; len = x.length; priv = proc->priv;
一元操作符與參數(shù)之間不留空格,。例如: x = *p; p = &x; i++; j = --i;
逗號(hào)右側(cè)若有內(nèi)容,,逗號(hào)與右側(cè)內(nèi)容之間應(yīng)有空格。例如: 分號(hào)右側(cè)若有內(nèi)容(右圓括號(hào)或另外一個(gè)分號(hào)除外),,分號(hào)與右側(cè)內(nèi)容之間應(yīng)有空格,。例如: for (i = 0; i < strlen(str);) { for (;;) { /* ... */ } }
圓括號(hào)內(nèi)部內(nèi)容與圓括號(hào)之間不留空格。例如: len = strlen(name);
for (i = 0; i < count; i++) { /* ... */ }
圓括號(hào)與左側(cè)關(guān)鍵字之間應(yīng)有空格,。例如: while (1) { /* ... */ }
if (i == 0) { /* ... */ }
圓括號(hào)與左側(cè)函數(shù)名之間不留空格,。例如: int load_file(const char *name) { foo(0); /* ... */ }
類型轉(zhuǎn)換中的圓括號(hào)與右側(cè)內(nèi)容之間不留空格,。例如: manager_priv_t *priv = (manager_priv_t *)p;
方括號(hào)與左側(cè)內(nèi)容、內(nèi)部內(nèi)容之間不留空格,。例如: 左花括號(hào)左側(cè)或右側(cè)若有內(nèi)容,,左右內(nèi)容與左花括號(hào)之間應(yīng)有空格。右花括號(hào)左側(cè)若有內(nèi)容,,左側(cè)內(nèi)容與右花括號(hào)之間應(yīng)有空格,;右花括號(hào)右側(cè)若有內(nèi)容(分號(hào)、逗號(hào)除外),,右側(cè)內(nèi)容與右花括號(hào)之間應(yīng)有空格,。例如: #define set_manager_index(x, idx) do { (x)->priv->index = (idx); } while (0)
manager_priv_t priv = { 0, NULL, };
分行定義的宏,\ 與左側(cè)內(nèi)容之間應(yīng)有空格,。例如: #define set_manager_index(x, idx) \ do { \ (x)->priv->index = (idx); \ } while (0)
3.6. 指針指針聲明或定義時(shí),,* 應(yīng)靠近變量名稱,。* 與修飾符之間應(yīng)有空格,。例如: int *p; const char *name; void * const ptr; void (*func)(void *arg);
3.7. 數(shù)值常量十六進(jìn)制數(shù)字A ~ F 使用大寫形式。表示二進(jìn)制的前綴0b 和表示十六進(jìn)制的0x 使用小寫形式,。后綴U 和L 使用大寫形式,。后綴f 使用小寫形式。表示冪的e 和p 使用小寫形式,。例如: unsigned int b = 0b0101; unsigned int x = 0xABCDEF; unsigned int u = 0U; long int l = 0L; unsigned long int ul = 0UL; float f = 1.0f; long double ld = 1.0L; double dd = -1.5e-5; double xd = 0xA.Bp12;
3.8. 注釋使用C90風(fēng)格的/* */ ,,不使用C++風(fēng)格的// 。 /* 或*/ 與注釋正文之間應(yīng)有空格,。行尾的注釋和代碼之間應(yīng)有空格,。完整語句注意首字母大寫和標(biāo)點(diǎn)符號(hào),簡單詞組可以不使用標(biāo)點(diǎn),。注意區(qū)分中英文標(biāo)點(diǎn),。 TODO:使用特定注釋格式可利用doxygen等自動(dòng)化工具生成文檔。例如: /* * This source file is part of AliOS Things. * Zhang San <[email protected]> * 2021.07.01 */
/* Zhang San <[email protected]> * 2021.07.01 */
/* This pointer must NOT be NULL. */
/* connecting */
4. 頭文件4.1. 路徑為避免與第三方庫的頭文件命名沖突,,公用組件的頭文件應(yīng)存放于子目錄中,,引用時(shí)路徑包含子目錄名稱。例如: 4.2. 引號(hào)和尖括號(hào)只有包含與本源文件處于同路徑中的頭文件時(shí)使用引號(hào),,其他情況均使用尖括號(hào),。例如: #include <stdio.h> #include 'my_demo.h'
4.3. 包含次序包含頭文件的次序如下:|次序 |種類| |-:- |:-| |1 |C語言標(biāo)準(zhǔn)庫頭文件和工具鏈頭文件| |2 |公用組件的頭文件| |3 |本工程頭文件| 例如: #include <stdio.h> #include <pthread.h> #include <openssl/rsa.h> #include <aos/headers.h> #include 'my_demo.h'
4.4. 保護(hù)所有頭文件都應(yīng)該使用#define 保護(hù)來防止被重復(fù)包含。相關(guān)宏命名格式是PATH_FILE_H ,。例如,,頭文件aos/common.h可按如下方法保護(hù): #ifndef AOS_COMMON_H #define AOS_COMMON_H
/* 全部內(nèi)容 */
#endif /* AOS_COMMON_H */
4.5. 函數(shù)、變量聲明頭文件中的函數(shù)聲明不使用extern 關(guān)鍵字,。頭文件中的全局變量聲明使用extern 關(guān)鍵字,。例如: void aos_cfg_file_close(int fd); extern char **aos_process_argv;
4.6. extern 'C'關(guān)鍵字公用頭文件中聲明的函數(shù)和全局變量應(yīng)該使用extern 'C' 關(guān)鍵字修飾,。 #include 不應(yīng)使用extern 'C' 關(guān)鍵字修飾。 #define ,、類型定義不作要求,,可酌情考慮。例如: #ifndef AOS_COMMON_H #define AOS_COMMON_H
#include <stddef.h>
#define AOS_STRING_MAX_LEN 127 #define AOS_LSTRING_MAX_LEN 511
typedef struct aos_tm { unsigned int sec; unsigned int min; unsigned int hour; unsigned int mday; unsigned int mon; unsigned int year; } aos_tm_t;
#ifdef __cplusplus extern 'C' { #endif
extern char aos_process_symbol[AOS_STRING_MAX_LEN + 1];
void aos_start(void);
#ifdef __cplusplus } #endif
#endif /* AOS_COMMON_H */
5. 其他注意事項(xiàng)只在本編譯單元使用的函數(shù),、全局變量應(yīng)使用static 修飾符,。在不影響功能的前提下,指針類型的函數(shù)參數(shù)盡量使用const 修飾符,。自增,、自減運(yùn)算符單獨(dú)使用時(shí)采用后置形式。數(shù)組,、結(jié)構(gòu)體初始化列表,、枚舉類型定義中的最后一個(gè)成員之后應(yīng)有逗號(hào)。例如: int offsets[] = { 0, 1, };
|