? 經常都會都會有讀者問如下類似的問題:
哪里有好的學習資源?
有值得學習和參考代碼嗎?
寫代碼怎么才能寫的漂亮?
? 其實,我們身邊就有很多“好的資源”值得學習,比如本文分享的?阿里 AliOS 的編碼風格。 ?
1. 前言
本文是AliOS Things提供的一套C語言代碼規范,適用的對象為符合C99標準的C語言工程。
2. 命名
本節內容均為建議,不作強制要求。
2.1. 總則
各種命名均使用英文單詞及其縮寫,非特殊情況不能使用漢語拼音或其他語言。
2.2. 文件命名
文件名全部使用小寫字母,用_連接。源文件使用.c后綴。頭文件使用.h后綴。
2.3. 類型命名
2.3.1. 簡單類型命名
使用typedef自定義的簡單類型命名全部使用小寫字母,用_連接,以_t結尾。例如:
?
?
typedef?int32_t?aos_status_t;
2.3.2. 結構體和聯合體命名
結構體和聯合體類型命名全部使用小寫字母,用_連接。建議使用typedef定義一個整體的名字,以_t結尾。例如:
?
?
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定義一個整體的名字,以_t結尾。枚舉值命名全部使用大寫字母,用_連接,包含表示類型的前綴。例如:
?
?
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. 變量命名
變量命名全部使用小寫字母,用_連接。數組名稱盡量使用復數名詞。例如:
?
?
cfg_file_t?cfg_files[NUM_CFG_FILES];
表示數目的變量名稱使用num(number的縮寫)加復數名詞。例如:
?
?
unsigned?int?num_files;
表示序號的變量名稱使用單數名詞加num或index或idx(index的縮寫)。例如:
?
?
unsigned?int?file_num; unsigned?int?file_index;
2.5. 函數命名
函數命名全部使用小寫字母,用_連接。
2.6. 宏命名
一般的宏命名全部使用大寫字母,用_連接。例如:
?
?
#define?AOS_STRING_MAX_LEN?127
模擬函數使用方式的宏的命名規則與函數相同。例如:
?
?
#define?aos_dev_set_id(dev,?x)? ????do?{? ????????(dev)->id?=?(x);? ????}?while?(0)
2.7. 前綴
為防止命名空間污染,公用組件中的非static函數、非static全局變量、全局類型、全局宏的命名應帶有前綴。例如(假設前綴為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風格換行格式。文本文件末尾應有且只有一個換行符,即末尾應有且只有一個空行。
3.2. 行長度
每行字符數原則上不超過120。包含長路徑的#include語句、頭文件#define保護可以無視此規則。
3.2.1. 表達式換行
較長的表達式可在運算符處換行,換行處的運算符屬于舊行,新行對齊到舊行中的相同邏輯層級。例如:
?
?
void?foo(void) { ????if?((aos_list_next(list_node)?!=?&list_head?&&?!priv)?|| ????????!(strcmp(symbol,?default_symbol)?&&?blahblahblahblahblahblah()?&& ??????????meomeomeomeomeomeomeomeomeomeomeomeomeomeomeomeo(NULL)))?{ ????????/*?...?*/ ????} }
3.2.2. 函數換行
較長的函數定義、聲明可在返回值類型和函數名稱之間換行。若返回值為指針類型,*屬于新行。例如:
?
?
static?unsigned?long blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah(void); static?const?manager_priv_t *blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah(int?index);
較長的函數定義、聲明、調用可在參數列表中間換行,參數列表中間換行后新行應縮進至舊行第一個參數處。例如:
?
?
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. 字符串換行
較長的字符串可在空格處換行,一般情況下換行處的空格屬于舊行。例如:
?
?
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. "); }
3.3. 縮進
使用空格縮進,每次4個空格。全文不應出現制表符(tab)。例如:
?
?
void?foo(unsigned?int?nbr_processes) { ????unsigned?int?i; ????while?(i?
宏定義、行尾注釋、結構體、聯合體、枚舉等內部可縮進實現多行對齊,但不作強制要求。若有縮進,應對齊到4的整數倍。例如:
?
?
/*?此處數字0縮進32個字符,即位于第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?{ ????/*?此處等號縮進32個字符,即位于第33列。?*/ ????SOCK_STG_DISCONNECTED???????=?0, ????SOCK_STG_CONNECTED??????????=?1, }?socket_stage_t; /*?此處反斜杠縮進40個字符,即位于第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) /*?此處注釋縮進24個字符,即位于第25列。?*/ foo(NULL);??????????????/*?abc?*/ blahblahblahblahblah();?/*?xyz?*/ /*?合法?*/ foofoofoo();?/*?abc?*/ foofoo();?/*?xyz?*/
分行定義的宏,第二行起應縮進一次。例如:
?
?
#define?aos_dev_set_id(dev,?x)? ????do?{? ????????(dev)->id?=?(x);? ????}?while?(0)
switch塊中的case語句和default語句與switch語句縮進層級相同。例如:
?
?
switch?(stage)?{ case?SOCK_STG_DISCONNECTED: ????foo(); ????break; case?SOCK_STG_CONNECTED: ????sock->connected?=?1; ????break; default: ????break; }
3.4. 花括號
函數體的左花括號另起一行;其他情況下左花括號不另起一行。一般情況下左花括號后續內容另起一行;宏定義中、數組、結構體、聯合體初始化時若花括號中內容較短則左花括號后續內容可以不另起一行。一般情況下右花括號另起一行;宏定義中、數組、結構體、聯合體初始化時若花括號中內容較短則右花括號可以不另起一行。右花括號與后續內容組合成一行。例如:
?
?
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. 空格
行尾不應有空格。三元操作符和二元操作符(獲取成員的.和->操作符除外)前后留有空格。例如:
?
?
x?=?a???b?:?c; v?=?w?*?x?+?y?/?z; len?=?x.length; priv?=?proc->priv;
一元操作符與參數之間不留空格。例如:
?
?
x?=?*p; p?=?&x; i++; j?=?--i;
逗號右側若有內容,逗號與右側內容之間應有空格。例如:
?
?
void?foo(int?x,?int?y);
分號右側若有內容(右圓括號或另外一個分號除外),分號與右側內容之間應有空格。例如:
?
?
for?(i?=?0;?i?
圓括號內部內容與圓括號之間不留空格。例如:
?
?
len?=?strlen(name); for?(i?=?0;?i?
圓括號與左側關鍵字之間應有空格。例如:
?
?
while?(1)?{ ????/*?...?*/ } if?(i?==?0)?{ ????/*?...?*/ }
圓括號與左側函數名之間不留空格。例如:
?
?
int?load_file(const?char?*name) { ????foo(0); ????/*?...?*/ }
類型轉換中的圓括號與右側內容之間不留空格。例如:
?
?
manager_priv_t?*priv?=?(manager_priv_t?*)p;
方括號與左側內容、內部內容之間不留空格。例如:
?
?
c?=?name[i];
左花括號左側或右側若有內容,左右內容與左花括號之間應有空格。右花括號左側若有內容,左側內容與右花括號之間應有空格;右花括號右側若有內容(分號、逗號除外),右側內容與右花括號之間應有空格。例如:
?
?
#define?set_manager_index(x,?idx)?do?{?(x)->priv->index?=?(idx);?}?while?(0) manager_priv_t?priv?=?{?0,?NULL,?};
分行定義的宏,與左側內容之間應有空格。例如:
?
?
#define?set_manager_index(x,?idx)? ????do?{? ????????(x)->priv->index?=?(idx);? ????}?while?(0)
3.6. 指針
指針聲明或定義時,*應靠近變量名稱。*與修飾符之間應有空格。例如:
?
?
int?*p; const?char?*name; void?*?const?ptr; void?(*func)(void?*arg);
3.7. 數值常量
十六進制數字A?~?F使用大寫形式。表示二進制的前綴0b和表示十六進制的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風格的/* */,不使用C++風格的//。?/*或*/與注釋正文之間應有空格。行尾的注釋和代碼之間應有空格。完整語句注意首字母大寫和標點符號,簡單詞組可以不使用標點。注意區分中英文標點。?TODO:使用特定注釋格式可利用doxygen等自動化工具生成文檔。例如:
?
?
/* ?*?This?source?file?is?part?of?AliOS?Things. ?*?Zhang?San?
4. 頭文件
4.1. 路徑
為避免與第三方庫的頭文件命名沖突,公用組件的頭文件應存放于子目錄中,引用時路徑包含子目錄名稱。例如:
?
?
#include?
4.2. 引號和尖括號
只有包含與本源文件處于同路徑中的頭文件時使用引號,其他情況均使用尖括號。例如:
?
?
#include?
4.3. 包含次序
包含頭文件的次序如下:|次序 |種類| |-:- |:-| |1 |C語言標準庫頭文件和工具鏈頭文件| |2 |公用組件的頭文件| |3 |本工程頭文件|
例如:
?
?
#include?
4.4. 保護
所有頭文件都應該使用#define保護來防止被重復包含。相關宏命名格式是PATH_FILE_H。例如,頭文件aos/common.h可按如下方法保護:
?
?
#ifndef?AOS_COMMON_H #define?AOS_COMMON_H /*?全部內容?*/ #endif?/*?AOS_COMMON_H?*/
4.5. 函數、變量聲明
頭文件中的函數聲明不使用extern關鍵字。頭文件中的全局變量聲明使用extern關鍵字。例如:
?
?
void?aos_cfg_file_close(int?fd); extern?char?**aos_process_argv;
4.6. extern "C"關鍵字
公用頭文件中聲明的函數和全局變量應該使用extern "C"關鍵字修飾。?#include不應使用extern "C"關鍵字修飾。?#define、類型定義不作要求,可酌情考慮。例如:
?
?
#ifndef?AOS_COMMON_H #define?AOS_COMMON_H #include?
5. 其他注意事項
只在本編譯單元使用的函數、全局變量應使用static修飾符。在不影響功能的前提下,指針類型的函數參數盡量使用const修飾符。自增、自減運算符單獨使用時采用后置形式。數組、結構體初始化列表、枚舉類型定義中的最后一個成員之后應有逗號。例如:
?
?
int?offsets[]?=?{ ????0, ????1, };
審核編輯:湯梓紅
評論
查看更多