• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 内核编码规范
2
3此规范基于业界通用的编程规范整理而成,请内核的开发人员遵守这样的编程风格。
4
5## 总体原则
6
7总体原则:
8
9- 清晰:代码应当易于理解、易于维护、易于重构,避免晦涩语法。
10
11- 简洁:命名简短,函数紧凑。
12
13- 高效:通过使用算法、编译器优化选项或硬件资源提高程序效率。
14
15- 美观:代码风格合理、一致。
16
17在大部分情况下,开发人员应当遵从以下规范,但也有一些例外场景。如修改第三方开源代码或大量使用开源代码接口下,应当与开源代码保持一致。请依据总体原则,灵活处理。
18
19## 目录结构
20
21建议按照功能模块划分子目录,子目录再定义头文件和源文件目录。
22
23目录名和文件名如果没有特殊的需要,采用全小写的形式,可以使用下划线(“_”)分割。
24
25## 命名
26
27推荐使用驼峰风格,具体规则如下:
28
29  | 类型 | 命名风格 | 形式 |
30| -------- | -------- | -------- |
31| 函数、结构体类型、枚举类型、联合体类型、typedef的类型。 | 大驼峰,或带有模块前缀的大驼峰。 | AaaBbb<br/>XXX_AaaBbb |
32| 局部变量,函数参数,宏参数,结构体中字段,联合体中成员。 | 小驼峰。 | aaaBBB |
33| 全局变量。 | 带“g_”前缀的小驼峰。 | g_aaaBBB |
34| 宏(不含函数式宏),枚举值,goto标签。 | 全大写,下划线分割。 | AAA_BBB |
35| 函数式宏。 | 全大写下划线分割,或大驼峰,或带有模块前缀的大驼峰。 | AAA_BBB<br/>AaaBbb<br/>XXX_AaaBbb |
36| 头文件防止重复的符号。 | 以下划线“_”开头以H结尾,中间为文件名的全大写并以下划线分割。 | _AAA_H |
37
38内核对外API建议采用LOS_ModuleFunc的形式,如果有宾语则建议采用前置的方式,比如:
39
40```
41LOS_TaskCreate
42LOS_MuxLock
43```
44
45kernel目录下内部模块间的接口使用OsModuleFunc的形式,比如:
46
47```
48OsTaskScan
49OsMuxInit
50```
51
52## 注释
53
54一般的,尽量通过清晰的软件架构,良好的符号命名来提高代码可读性;在需要的时候,辅以注释说明。
55
56注释是为了帮助阅读者快速读懂代码,所以要从读者的角度出发,按需注释。
57
58注释内容要简洁、明了、无歧义,信息全面且不冗余。
59
60文件头部要进行注释,建议注释列出:版权说明、文件功能说明,作者、创建日期、注意事项等。
61
62注释风格要统一,建议优先选择/\* \*/的方式,注释符与注释内容之间要有1空格,单行、多行注释风格如下:
63
64```
65/* 单行注释 */
66// 单行注释。
67/*
68 * 多行注释
69 * 第二行
70 */
71// 多行注释。
72// 另一行。
73```
74
75针对代码的注释,应该置于对应代码的上方或右方。
76
77代码上方的注释,与代码行间无空行,保持与代码一样的缩进。
78
79代码右边的注释,与代码之间,至少留1空格。
80
81建议将多条连续的右侧注释对齐,比如:
82
83```
84#define CONST_A 100    /* Const A */
85#define CONST_B 2000   /* Const B */
86```
87
88## 格式
89
90程序采用缩进风格编写,使用空格而不是制表符(’\t’)进行缩进,每级缩进为4个空格。
91
92换行时,函数左大括号另起一行放行首,并独占一行;其他左大括号跟随语句放行末。右大括号独占一行,除非后面跟着同一语句的剩余部分,如 do 语句中的 while,或者 if 语句的else/else if,或者逗号、分号。
93
94一行只写一条语句。
95
96比如:
97
98```
99struct MyType {  // 跟随语句放行末,前置1空格。
100    // ...
101};               // 右大括号后面紧跟分号。
102int Foo(int a) { // 函数左大括号独占一行,放行首。
103    if (a > 0) {
104        Foo();   // 一行只有一条语句。
105        Bar();
106     } else {    // 右大括号、"else"、以及后续的左大括号均在同一行。
107        // ...
108     }           // 右大括号独占一行。
109     // ...
110}
111```
112
113每行字符数不要超过 120 个,代码过长时应当换行,换行时将操作符留在行末,新行缩进一层或进行同类对齐,并将表示未结束的操作符或连接符号留在行末。
114
115```
116// 假设下面第一行已经不满足行宽要求。
117if (currentValue > MIN && // Good:换行后,布尔操作符放在行末。
118    currentValue < MAX) { // Good: 与(&&)操作符的两个操作数同类对齐。
119       DoSomething();
120       // ...
121}
122flashPara.flashEndAddr = flashPara.flashBaseAddr + // Good: 加号留在行末。
123                         flashPara.flashSize; // Good: 加法两个操作数对齐。
124
125// Good:函数参数放在一行。
126ReturnType result = FunctionName(paramName1, paramName2);
127ReturnType result = FunctionName(paramName1,
128                                 paramName2,
129                                 paramName3); // Good:保持与上方参数对齐。
130ReturnType result = FunctionName(paramName1, paramName2,
131      paramName3, paramName4, paramName5); // Good:参数换行,4 空格缩进。
132ReturnType result = VeryVeryVeryLongFunctionName( // 行宽不满足第1个参数,直接换行。
133      paramName1, paramName2, paramName3); // 换行后,4 空格缩进。
134
135// Good:每行的参数代表一组相关性较强的数据结构,放在一行便于理解。
136int result = DealWithStructLikeParams(left.x, left.y,    // 表示一组相关参数。
137                                      right.x, right.y); // 表示另外一组相关参数。
138```
139
140包括 if/for/while/do-while 语句应使用大括号,即复合语句。
141
142```
143while (condition) {} // Good:即使循环体是空,也应使用大括号。
144while (condition) {
145     continue;       // Good:continue 表示空逻辑,使用大括号。
146}
147```
148
149case/default 语句相对 switch 缩进一层,风格如下:
150
151```
152switch (var) {
153    case 0:             // Good: 缩进。
154        DoSomething1(); // Good: 缩进。
155        break;
156    case 1: {           // Good: 带大括号格式。
157        DoSomething2();
158        break;
159    }
160    default:
161        break;
162}
163```
164
165指针类型"\*"跟随变量或者函数名,例如:
166
167```
168int *p1;   // OK
169int* p2;   // Bad:跟随类型。
170int*p3;    // Bad:两边都没空格。
171int * p4;  // Bad:两边都有空格。
172struct Foo *CreateFoo(void); // OK: "*"跟随函数名。
173特例:
174char * const VERSION = "V100";    // OK: 当有 const 修饰符时,"*"两边都有空格。
175int Foo(const char * restrict p); // OK: 当有 restrict 修饰符时,"*"两边都有空格。
176sz = sizeof(int*); // OK:右侧没有变量,"*"跟随类型。
177```
178
179## 宏
180
181定义函数式宏前,应考虑能否用函数替代。对于可替代场景,建议用函数替代宏。对于有性能需求的场景,可以使用内联函数。
182
183定义宏时,要使用完备的括号,例如:
184
185```
186#define SUM(a, b) ((a) + (b))   // 符合本规范要求。
187#define SOME_CONST  100         // Good: 单独的数字无需括号。
188#define ANOTHER_CONST   (-1)    // Good: 负数需要使用括号。
189#define THE_CONST   SOME_CONST  // Good: 单独的标识符无需括号。
190```
191
192以下情况需要注意:
193
194- 宏参数参与 '\#', '\#\#' 操作时,不要加括号;
195
196- 宏参数参与字符串拼接时,不要加括号;
197
198- 宏参数作为独立部分,在赋值(包括+=, -=等)操作的某一边时,可以不加括号;
199
200- 宏参数作为独立部分,在逗号表达式,函数或宏调用列表中,可以不加括号。
201
202```
203// x 不要加括号。
204#define MAKE_STR(x) #x
205
206// obj 不要加括号。
207#define HELLO_STR(obj) "Hello, " obj
208
209// a, b 需要括号;而 value 可以不加括号。
210#define UPDATE_VALUE(value, a, b) (value = (a) + (b))
211
212// a 需要括号;而 b 可以不加括号。
213#define FOO(a, b) Bar((a) + 1, b)
214```
215
216包含多条语句的函数式宏的实现语句必须放在 do-while(0)中。
217
218禁止把带副作用的表达式作为参数传递给函数式宏,比如自加操作(“a++”)。
219
220函数式宏定义中慎用 return、goto、continue、break 等改变程序流程的语句。
221
222禁止宏调用参数中出现预编译指令,如\#include,\#define和\#ifdef,这样做会导致未定义的行为。
223
224宏定义不以分号结尾。
225
226## 头文件
227
228头文件应当职责单一。
229
230通常情况下,每个.c文件都应有一个相应的.h文件(并不一定同名),用于放置对外提供的函数声明、宏定义、类型定义等。如果不需要提供对外接口,可以没有对应的.h文件。
231
232避免头文件循环依赖,如a.h 包含 b.hb.h 包含 c.hc.h 包含 a.h233
234头文件应当自包含,即包含某个头文件,不需要引入其他头文件就可以编译。
235
236头文件用\#define、\#ifndef、\#endif保护,防止重复包含;不要使用 \#pragma once。
237
238禁止通过声明的方式引用外部函数接口、变量,只能通过包含头文件的方式使用其他模块或文件提供的接口。
239
240建议按稳定度包含头文件,依次顺序为: 源码对应的头文件,C标准库,操作系统库,平台库,项目公共库,自己其他的依赖。
241
242## 数据类型
243
244基础数据类型建议使用los_compiler.h中定义的类型,比如无符号32位整数位定义为UINT32。
245
246## 变量
247
248避免大量栈分配,如较大的局部数组。
249
250谨慎使用全局变量,尽量不用或少用全局变量。
251
252变量应当初始化后再使用。
253
254禁止将局部变量的地址返回到其作用域以外。
255
256指向资源句柄或描述符的变量,在资源释放后立即赋予新值(如果变量的作用域马上结束可以不赋予新值)。指向资源句柄或描述符的变量包括指针、文件描述符、socket描述符以及其它指向资源的变量。
257
258## 断言
259
260断言必须使用宏定义,且只能在调试版本中生效。
261
262断言应当看作设计约束,禁止用断言检测程序在运行期间可能导致的错误,可能发生的错误要用错误处理代码来处理。
263
264禁止在断言内改变运行环境。
265
266一个断言只用于检查一个错误。
267
268## 函数
269
270由一个进程向另一个进程发送的数据、由应用向内核发送的数据等应当进行合法性校验,校验包括但不限于:
271
272- 校验数据长度
273
274- 校验数据范围
275
276- 校验数据类型和格式
277
278- 校验输入只包含可接受的字符(“白名单”形式)
279
280函数应避免使用全局变量、静态局部变量和直接的I/O操作,不可避免时,应当对读写操作进行封装。
281