1@page coding_style 代码风格 2 3**[更正文档](https://gitee.com/alios-things/documentation/edit/rel_3.3.0/coding/coding_style.md)**      **[贡献说明](https://help.aliyun.com/document_detail/302301.html)** 4 5# 1. 前言 6本文是AliOS Things提供的一套C语言代码规范,适用的对象为符合C99标准的C语言工程。 7 8# 2. 命名 9本节内容均为建议,不作强制要求。 10 11## 2.1. 总则 12各种命名均使用英文单词及其缩写,非特殊情况不能使用汉语拼音或其他语言。 13 14## 2.2. 文件命名 15文件名全部使用小写字母,用`_`连接。 16源文件使用`.c`后缀。 17头文件使用`.h`后缀。 18 19## 2.3. 类型命名 20### 2.3.1. 简单类型命名 21使用`typedef`自定义的简单类型命名全部使用小写字母,用`_`连接,以`_t`结尾。例如: 22```c 23typedef int32_t aos_status_t; 24``` 25 26### 2.3.2. 结构体和联合体命名 27结构体和联合体类型命名全部使用小写字母,用`_`连接。建议使用`typedef`定义一个整体的名字,以`_t`结尾。例如: 28```c 29typedef struct aos_list_node { 30 struct aos_list_node *prev; 31 struct aos_list_node *next; 32} aos_list_node_t; 33 34static aos_list_node_t list_node; 35``` 36 37### 2.3.3. 枚举命名 38枚举类型命名全部使用小写字母,用`_`连接。建议使用`typedef`定义一个整体的名字,以`_t`结尾。 39枚举值命名全部使用大写字母,用`_`连接,包含表示类型的前缀。 40例如: 41```c 42typedef enum aos_socket_stage { 43 AOS_SOCK_STG_DISCONNECTED, 44 AOS_SOCK_STG_CONNECTED, 45} aos_socket_stage_t; 46 47static aos_socket_stage_t sock_stage = AOS_SOCK_STG_DISCONNECTED; 48``` 49 50## 2.4. 变量命名 51变量命名全部使用小写字母,用`_`连接。 52数组名称尽量使用复数名词。例如: 53```c 54cfg_file_t cfg_files[NUM_CFG_FILES]; 55``` 56表示数目的变量名称使用num(number的缩写)加复数名词。例如: 57```c 58unsigned int num_files; 59``` 60表示序号的变量名称使用单数名词加num或index或idx(index的缩写)。例如: 61```c 62unsigned int file_num; 63unsigned int file_index; 64``` 65 66## 2.5. 函数命名 67函数命名全部使用小写字母,用`_`连接。 68 69## 2.6. 宏命名 70一般的宏命名全部使用大写字母,用`_`连接。例如: 71```c 72#define AOS_STRING_MAX_LEN 127 73``` 74模拟函数使用方式的宏的命名规则与函数相同。例如: 75```c 76#define aos_dev_set_id(dev, x) \ 77 do { \ 78 (dev)->id = (x); \ 79 } while (0) 80``` 81## 2.7. 前缀 82为防止命名空间污染,公用组件中的非static函数、非static全局变量、全局类型、全局宏的命名应带有前缀。例如(假设前缀为`aos`): 83```c 84void aos_cfg_file_close(int fd); 85extern char **aos_process_argv; 86typedef struct aos_list_node aos_list_node_t; 87#define AOS_STRING_MAX_LEN 127 88``` 89 90# 3. 格式 91## 3.1. 文本格式 92源文件、头文件、Makefile等文本文件一律采用UTF-8 without BOM编码,采用Unix风格换行格式。 93文本文件末尾应有且只有一个换行符,即末尾应有且只有一个空行。 94 95## 3.2. 行长度 96每行字符数原则上不超过120。包含长路径的`#include`语句、头文件`#define`保护可以无视此规则。 97 98### 3.2.1. 表达式换行 99较长的表达式可在运算符处换行,换行处的运算符属于旧行,新行对齐到旧行中的相同逻辑层级。例如: 100```c 101void foo(void) 102{ 103 if ((aos_list_next(list_node) != &list_head && !priv) || 104 !(strcmp(symbol, default_symbol) && blahblahblahblahblahblah() && 105 meomeomeomeomeomeomeomeomeomeomeomeomeomeomeomeo(NULL))) { 106 /* ... */ 107 } 108} 109``` 110 111### 3.2.2. 函数换行 112较长的函数定义、声明可在返回值类型和函数名称之间换行。若返回值为指针类型,`*`属于新行。例如: 113```c 114static unsigned long 115blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah(void); 116static const manager_priv_t 117*blahblahblahblahblahblahblahblahblahblahblahblahblahblahblahblah(int index); 118``` 119较长的函数定义、声明、调用可在参数列表中间换行,参数列表中间换行后新行应缩进至旧行第一个参数处。例如: 120```c 121void blahblahblahblahblahblahblahblahblah(manager_priv_t *priv, int index, 122 const char *proc_name); 123 124void foo(void) 125{ 126 blahblahblahblahblahblahblahblahblahblah(get_manager_priv(manager), 0, 127 "meomeomeomeomeomeomeomeo"); 128} 129``` 130 131### 3.2.3. 字符串换行 132较长的字符串可在空格处换行,一般情况下换行处的空格属于旧行。例如: 133```c 134void foo(void) 135{ 136 printf("The GNU operating system consists of GNU packages " 137 "(programs specifically released by the GNU Project) " 138 "as well as free software released by third parties.\n"); 139} 140``` 141 142## 3.3. 缩进 143使用空格缩进,每次4个空格。全文不应出现制表符(tab)。例如: 144```c 145void foo(unsigned int nbr_processes) 146{ 147 unsigned int i; 148 149 while (i < nbr_processes) { 150 const char *name; 151 /* ... */ 152 } 153} 154``` 155宏定义、行尾注释、结构体、联合体、枚举等内部可缩进实现多行对齐,但不作强制要求。若有缩进,应对齐到4的整数倍。例如: 156```c 157/* 此处数字0缩进32个字符,即位于第33列。 */ 158#define STAGE_UPDATE_CONTINUE 0 159#define STAGE_UPDATE_COMPLETE 1 160 161/* 合法 */ 162#define EVENT_RX_FULL (1U << 0) 163#define EVENT_TX_EMPTY (1U << 1) 164 165typdef enum socket_stage { 166 /* 此处等号缩进32个字符,即位于第33列。 */ 167 SOCK_STG_DISCONNECTED = 0, 168 SOCK_STG_CONNECTED = 1, 169} socket_stage_t; 170 171/* 此处反斜杠缩进40个字符,即位于第41列。 */ 172#define aos_dev_set_flags(dev, x) \ 173 do { \ 174 (dev)->flags = (x); \ 175 } while (0) 176 177/* 合法 */ 178#define aos_dev_set_ops(dev, x) \ 179 do { \ 180 (dev)->ops = (x); \ 181 } while (0) 182 183/* 此处注释缩进24个字符,即位于第25列。 */ 184foo(NULL); /* abc */ 185blahblahblahblahblah(); /* xyz */ 186 187/* 合法 */ 188foofoofoo(); /* abc */ 189foofoo(); /* xyz */ 190``` 191分行定义的宏,第二行起应缩进一次。例如: 192```c 193#define aos_dev_set_id(dev, x) \ 194 do { \ 195 (dev)->id = (x); \ 196 } while (0) 197``` 198`switch`块中的`case`语句和`default`语句与`switch`语句缩进层级相同。例如: 199```c 200switch (stage) { 201case SOCK_STG_DISCONNECTED: 202 foo(); 203 break; 204case SOCK_STG_CONNECTED: 205 sock->connected = 1; 206 break; 207default: 208 break; 209} 210``` 211 212## 3.4. 花括号 213函数体的左花括号另起一行;其他情况下左花括号不另起一行。一般情况下左花括号后续内容另起一行;宏定义中、数组、结构体、联合体初始化时若花括号中内容较短则左花括号后续内容可以不另起一行。 214一般情况下右花括号另起一行;宏定义中、数组、结构体、联合体初始化时若花括号中内容较短则右花括号可以不另起一行。右花括号与后续内容组合成一行。 215例如: 216```c 217typedef struct manager_priv { 218 int index; 219 void *data; 220} manager_priv_t; 221 222#define set_manager_index(x, idx) do { (x)->priv->index = (idx); } while (0) 223 224#ifdef __cplusplus 225extern "C" { 226#endif 227 228void foo(void) 229{ 230 int i = 0; 231 232 /* ... */ 233 234 if (i == 0) { 235 /* ... */ 236 } else { 237 /* ... */ 238 } 239} 240 241manager_priv_t priv = { 0, NULL, }; 242 243#ifdef __cplusplus 244} 245#endif 246``` 247 248## 3.5. 空格 249行尾不应有空格。 250三元操作符和二元操作符(获取成员的`.`和`->`操作符除外)前后留有空格。例如: 251```c 252x = a ? b : c; 253v = w * x + y / z; 254len = x.length; 255priv = proc->priv; 256``` 257一元操作符与参数之间不留空格。例如: 258```c 259x = *p; 260p = &x; 261i++; 262j = --i; 263``` 264逗号右侧若有内容,逗号与右侧内容之间应有空格。例如: 265```c 266void foo(int x, int y); 267``` 268分号右侧若有内容(右圆括号或另外一个分号除外),分号与右侧内容之间应有空格。例如: 269```c 270for (i = 0; i < strlen(str);) { 271 for (;;) { 272 /* ... */ 273 } 274} 275``` 276圆括号内部内容与圆括号之间不留空格。例如: 277```c 278len = strlen(name); 279 280for (i = 0; i < count; i++) { 281 /* ... */ 282} 283``` 284圆括号与左侧关键字之间应有空格。例如: 285```c 286while (1) { 287 /* ... */ 288} 289 290if (i == 0) { 291 /* ... */ 292} 293``` 294圆括号与左侧函数名之间不留空格。例如: 295```c 296int load_file(const char *name) 297{ 298 foo(0); 299 /* ... */ 300} 301``` 302类型转换中的圆括号与右侧内容之间不留空格。例如: 303```c 304manager_priv_t *priv = (manager_priv_t *)p; 305``` 306方括号与左侧内容、内部内容之间不留空格。例如: 307```c 308c = name[i]; 309``` 310左花括号左侧或右侧若有内容,左右内容与左花括号之间应有空格。右花括号左侧若有内容,左侧内容与右花括号之间应有空格;右花括号右侧若有内容(分号、逗号除外),右侧内容与右花括号之间应有空格。例如: 311```c 312#define set_manager_index(x, idx) do { (x)->priv->index = (idx); } while (0) 313 314manager_priv_t priv = { 0, NULL, }; 315``` 316分行定义的宏,`\`与左侧内容之间应有空格。例如: 317```c 318#define set_manager_index(x, idx) \ 319 do { \ 320 (x)->priv->index = (idx); \ 321 } while (0) 322``` 323 324## 3.6. 指针 325指针声明或定义时,`*`应靠近变量名称。`*`与修饰符之间应有空格。例如: 326```c 327int *p; 328const char *name; 329void * const ptr; 330void (*func)(void *arg); 331``` 332 333## 3.7. 数值常量 334十六进制数字`A` ~ `F`使用大写形式。 335表示二进制的前缀`0b`和表示十六进制的`0x`使用小写形式。 336后缀`U`和`L`使用大写形式。 337后缀`f`使用小写形式。 338表示幂的`e`和`p`使用小写形式。 339例如: 340```c 341unsigned int b = 0b0101; 342unsigned int x = 0xABCDEF; 343unsigned int u = 0U; 344long int l = 0L; 345unsigned long int ul = 0UL; 346float f = 1.0f; 347long double ld = 1.0L; 348double dd = -1.5e-5; 349double xd = 0xA.Bp12; 350``` 351 352## 3.8. 注释 353使用C90风格的`/* */`,不使用C++风格的`//`。 354`/*`或`*/`与注释正文之间应有空格。行尾的注释和代码之间应有空格。 355完整语句注意首字母大写和标点符号,简单词组可以不使用标点。注意区分中英文标点。 356**TODO:**使用特定注释格式可利用doxygen等自动化工具生成文档。 357例如: 358```c 359/* 360 * This source file is part of AliOS Things. 361 * Zhang San <zhangsan@abc.com> 362 * 2021.07.01 363 */ 364 365/* Zhang San <zhangsan@abc.com> 366 * 2021.07.01 */ 367 368/* This pointer must NOT be NULL. */ 369 370/* connecting */ 371``` 372 373# 4. 头文件 374## 4.1. 路径 375为避免与第三方库的头文件命名冲突,公用组件的头文件应存放于子目录中,引用时路径包含子目录名称。例如: 376```c 377#include <aos/file.h> 378``` 379 380## 4.2. 引号和尖括号 381只有包含与本源文件处于同路径中的头文件时使用引号,其他情况均使用尖括号。例如: 382```c 383#include <stdio.h> 384#include "my_demo.h" 385``` 386 387## 4.3. 包含次序 388包含头文件的次序如下: 389|次序 |种类| 390|-:- |:-| 391|1 |C语言标准库头文件和工具链头文件| 392|2 |公用组件的头文件| 393|3 |本工程头文件| 394 395例如: 396```c 397#include <stdio.h> 398#include <pthread.h> 399#include <openssl/rsa.h> 400#include <aos/headers.h> 401#include "my_demo.h" 402``` 403 404## 4.4. 保护 405所有头文件都应该使用`#define`保护来防止被重复包含。相关宏命名格式是`PATH_FILE_H`。 406例如,头文件*aos/common.h*可按如下方法保护: 407 408```c 409#ifndef AOS_COMMON_H 410#define AOS_COMMON_H 411 412/* 全部内容 */ 413 414#endif /* AOS_COMMON_H */ 415``` 416 417## 4.5. 函数、变量声明 418头文件中的函数声明不使用`extern`关键字。头文件中的全局变量声明使用`extern`关键字。例如: 419```c 420void aos_cfg_file_close(int fd); 421extern char **aos_process_argv; 422``` 423 424## 4.6. extern "C"关键字 425公用头文件中声明的函数和全局变量应该使用`extern "C"`关键字修饰。 426`#include`不应使用`extern "C"`关键字修饰。 427`#define`、类型定义不作要求,可酌情考虑。 428例如: 429```c 430#ifndef AOS_COMMON_H 431#define AOS_COMMON_H 432 433#include <stddef.h> 434 435#define AOS_STRING_MAX_LEN 127 436#define AOS_LSTRING_MAX_LEN 511 437 438typedef struct aos_tm { 439 unsigned int sec; 440 unsigned int min; 441 unsigned int hour; 442 unsigned int mday; 443 unsigned int mon; 444 unsigned int year; 445} aos_tm_t; 446 447#ifdef __cplusplus 448extern "C" { 449#endif 450 451extern char aos_process_symbol[AOS_STRING_MAX_LEN + 1]; 452 453void aos_start(void); 454 455#ifdef __cplusplus 456} 457#endif 458 459#endif /* AOS_COMMON_H */ 460``` 461 462# 5. 其他注意事项 463只在本编译单元使用的函数、全局变量应使用`static`修饰符。 464在不影响功能的前提下,指针类型的函数参数尽量使用`const`修饰符。 465自增、自减运算符单独使用时采用后置形式。 466数组、结构体初始化列表、枚举类型定义中的最后一个成员之后应有逗号。例如: 467```c 468int offsets[] = { 469 0, 470 1, 471}; 472``` 473打印到终端的信息一律使用英文,以避免终端编码问题。 474