规范原则¶
- 朴实,避免晦涩语法
- 严谨,逻辑反复思考
- 简约,命名简洁、代码精炼
- 性能,通过算法、编译器、硬件进行优化
目录结构¶
原则:
整个工程按照功能模块划分子目录,每个子目录再划分头文件和源文件目录,以便架构清晰、易懂。
示例:
在比较复杂的项目时,建议目录结构如下:
在项目相对简单时,建议目录结构如下:
系统组件¶
目录设计¶
原则:
目录的命名能准确描述模块的基本功能,建议用小写字母且不含下划线、点等特殊符号;
目录必须放于相包含的父目录之下,并需要明确与其他目录间的耦合性。
示例:
- kernel:系统内核部分;
- libs:依赖库;
- mqtt:mqtt协议库;
- external/mbedtls:位于第三方库目录的mbedtls库
依赖关系¶
原则:
新添加组件往往依赖于系统原有组件,必须以最小耦合的方式明确所直接依赖的组件。
示例:
略
头文件¶
文件命名¶
原则:
头文件命名能准确描述文件所包含的模块内容,达到通俗、易懂的目的。
示例:
略
版权声明(阿里集团内部)¶
原则:
项目内的所有头文件(除引入的第三方库)版权声明一致,并且在文件内顶格注释。
示例:
如AliOS版权声明如下:
/* * Copyright (C) 2015-2017 Alibaba Group Holding Limited */
引用保护¶
原则:
项目内的所有头文件采用#define宏防止多重包含,命名格式为:
XXX_FILENAME_H
Note
为了保证宏定义的惟一性,建议“XXX”命名为头文件目录名、路径名或有意义的其他符号(如“k”: kernel)。
示例:
如下图,在版权声明后空一行,且在“#endif”后空一行且添加注释(注释内容两边各留一个空格且采用“/* */”格式)。
/* * Copyright (C) 2015-2017 Alibaba Group Holding Limited */ #ifndef K_ERR_H #define K_ERR_H #endif /* K_ERR_H */
引用方式¶
原则:
系统头文件和第三方库通过“<.h>”方式引用,其他文件通过“.h”引用。
示例:
略
函数定义¶
原则:
由于C++编译器在编译时与C编译器命名不同,为了保证C++代码能够正确调用C接口, 建议在C语言接口声明时采用extern的方式指示编译器按C语言去编译。
示例:
如下图,在extern的声明代码上下各添加一行空行。
/* * Copyright (C) 2015-2017 Alibaba Group Holding Limited */ #ifndef K_ERR_H #define K_ERR_H #ifdef __cplusplus extern "C" { #endif #ifdef __cplusplus } #endif #endif /* K_ERR_H */
源文件¶
文件命名¶
原则:
源文件命名能准确描述文件所包含的模块内容,达到通俗、易懂的目的。
示例:
略
版权声明¶
原则:
项目内的所有头文件(除引入的第三方库)版权声明一致,并且在文件内顶格注释。
示例:
如AliOS版权声明如下:
/* * Copyright (C) 2015-2017 Alibaba Group Holding Limited */
头文件依赖¶
原则:
在源文件中引入头文件时采用如下顺序:C库、C++库、其他第三方库、项目内头文件, 同一类型的头文件采用字母顺序排列,避免引入冗余头文件。
示例:
略
函数定义¶
原则:
源文件中函数的定义顺序严格按照头文件中声明的顺序排列,且内部函数添加static关键字,比较短的函数还需添加inline关键字。 对外函数必须进行参数检查,内部函数不做检查参数要求。返回值必须类型匹配。
示例:
略
命名风格¶
原则¶
- 统一,相同含义的单词、命名方式项目内统一。
- 准确,采用计算机领域常用单词。
- 风格,采用字符加下划线的方式(不使用驼峰风格)
缩写¶
原则:
建议采用计算机领域常用缩写以便清晰表达含义,相关缩写可[参考](http://www.abbreviations.com/abbreviation/DISABLED)。
示例:
略
类型命名¶
原则:
数据结构类型以“_t”结尾,枚举变量类型以“_e”结尾,回调函数类型以“_cb”结尾;或所有类型以“_t”作为后缀。字符均小写。
示例:
typedef int (*xxx0_cb)(...); typedef struct { ... } xxx1_t; typedef struct xxx2_s { struct xxx2_s *x; ... } xxx2_t; typedef enum { ... } xxx3_e;
常量命名¶
原则:
常量命名各字段必须大写,且字段数不超过5个。
示例:
略
枚举命名¶
原则:
枚举命名各字段必须大写,且字段数`不超过5个`。
示例:
略
宏命名¶
原则:
宏命名各字段必须大写,且字段数`不超过5个`。
示例:
略
注释方式¶
函数注释¶
原则:
对外接口按doxgen要求的格式书写,且注释和函数定义间不留空行; 对内接口在需要时添加注释,注释位于定义前。
示例:
变量注释¶
原则:
注释在变量定义后,在仅有一个需要注释的变量时与分号之间只留一个空格; 若多行变量定义均需注释,则最长变量定义留一个空格,其他注释与其对齐。
示例:
编译宏注释¶
原则:
注释在宏定义后,在仅有一个需要注释的宏时与分号之间只留一个空格; 若多行宏定义均需注释,则最长宏定义后留一个空格,其他注释与其对齐。
示例:
所有的缩进必须使用空格(除makefile外不允许使用tab),且以`四个空格`为单位进行缩进。 嵌套的编译条件判断顶格写,且需要在“#endif”后的注释中备注属于哪个判断条件。
示例:
#ifdef XXX int func1(void) { ... #ifdef XX func(); #endif /* XX */ ... } #endif /* XXX */
空行¶
原则:
在所有需要空行的地方仅空一行,如版权声明与代码之间,不同类型的头文件引用之间,函数定义之间等。
示例:
略
变量声明¶
原则:
全局变量的声明必须放在相关头文件中,且单个声明的类型和变量之间留一个空格,多个声明时按最长的留一个空格对齐。
示例:
extern int g_x_x; /* ... */ extern int g_xx_xx; /* ... */ extern long g_xxx_xxx; /* ... */ extern char *g_xxxx_xxxx; /* ... */
变量定义¶
原则:
全局变量的定义必须放在相关的源文件中,且在头文件和函数定义之间; 局部变量的定义必须放在函数作用域的最开始处; 变量定义的类型和变量之间(或者“*”)留一个空格,当有多个变量时按照最长原则对齐。
示例:
Note
上图中描述了四种情况下的对齐方式
结构体声明¶
原则:
在头文件中引用其他项目内头文件的结构体定义时采用extern进行声明,不引入相关头文件。
示例:
略
结构体定义¶
原则:
结构体定义放在相关头文件中,并且在函数声明前,结构体定义前后需要各空一行。
示例:
略
函数声明¶
原则:
对外函数的声明必须放在头文件中,且在头文件中按照关联性排序; 当每行超过`80列`时需将参数移到下一行且与上一行参数首字符对齐; 函数内紧邻左右大括号的参数不需要空格,但参数之间需要留一个空格; 仅一个函数时,返回类型与函数定义之间空一格,当多个关联函数时,返回最长的类型与函数定义之间空一格,其他关联函数首字符与其对齐。 对内声明的参数除放在源文件中外,其他与对外声明的函数保持一致。
示例:
命名顺序、空格、空行、参数换行请参考下图。
int xxx_xxx_func1(void); int xxx_xxx_func2(void); int xxx_xxx_func3(...); int xxx_xxx_func4(...); void xxx_xxx_func5(...); void *xxx_xxx_func6(...);
函数定义¶
原则:
函数的定义原则上在源文件中实现,且其定义的顺序与声明保持一致; 在函数定义时,需要将大括号另起一行,且代码逻辑紧邻大括号后一行; 函数之前留一空行。
示例:
int xxx_func1(void) { return 0; } int xxx_func2(void) { return 0 } int xxx_func3(void) { return 0; }
函数调用¶
原则:
函数调用风格与定义基本相同,在超过`80列`时按照相同的规范处理; 需要对函数调用的返回值进行判断,且判断语句与上一条语句之间不留空行。
示例:
int xxx_func(void) { int ret; ret = xxx_func1(...); if (ret != 0) { ... return ret; } return 0; }
条件语句¶
原则:
条件判断必须明确,如“if (var)”形式应该写为“if (var != 0)”; 指针是否为空时必须与NULL进行对比(不建议用0),整型返回值可通过与1/0进行对比。
示例:
if (var != 0) { return -1; } if (ret != 0) { ... return -2; }
循环语句¶
原则:
风格与条件判断语句类似。
示例:
for (i = 0; i < 100; i++) { ... } while (ret == 1) { ... } do { ... } while(ret != 1);