当前位置:网站首页>编程规范——LiteOS
编程规范——LiteOS
2022-08-02 14:02:00 【CynalFly】
此编程规范在业界通用的编程规范基础上进行了整理,供开发者参考使用。
总体原则
- 清晰,易于维护、易于重构
- 简洁,易于理解,并且易于实现
- 风格统一,代码整体风格保持统一
- 通用性,遵循业界通用的编程规范
目录结构
建议将工程按照功能模块划分子目录(可参考LiteOS的功能模块划分),子目录再定义头文件和源文件目录。
命名
- 使用驼峰风格进行命名,此风格大小写字母混用,不同单词间通过单词首字母大写来分开,具体规则如下:
表1 驼峰命名风格规则 类型
命名风格
形式
函数,自定义的类型
大驼峰,或带有模块前缀的大驼峰
AaaBbb, XXX_AaaBbb
局部变量,函数参数,宏参数,结构体成员,联合体成员
小驼峰
aaaBbb
全局变量
带'g_'前缀的小驼峰
g_aaaBbb
宏,枚举值
全大写并下划线分割
AAA_BBB
内核头文件中防止重复包含的宏变量
带'_LOS'前缀和'H'后缀,中间为大写模块名,以下划线分割
_LOS_MODULE_H
- 全局函数、全局变量、宏、类型名、枚举名的命名,应当准确描述并全局唯一。
- 在能够准确表达含义的前提下,局部变量,或结构体、联合体的成员变量,其命名应尽可能简短。LiteOS的对外API使用LOS_<Module><Func>的方式,如果有宾语采用前置的方式,比如:
kernel目录下内部模块间接口使用Os<Module><Func>的方式,比如:LOS_TaskCreate LOS_SwtmrStart LOS_SemPend
OsTaskScan OsSwtmrStart
arch目录需要给上层模块提供Low Level接口,这部分接口采用Arch<Module><Func>的方式。 其他情况可采用<Module><Func>的方式。
排版与格式
- 程序块采用缩进风格编写,使用空格而不是制表符('\t')进行缩进,每级缩进为4个空格。
- 采用K&R风格作为大括号换行风格,即函数左大括号另起一行放行首,并独占一行,其他左大括号跟随语句放行末, 右大括号独占一行,除非后面跟着同一语句的剩余部分,如if语句的else/else if或者分号,比如:
struct MyType { // 左大括号跟随语句放行末,前置1个空格
...
}; // 右大括号后面紧跟分号
int Foo(int a)
{ // 函数左大括号独占一行,放行首
if (a > 0) { // 左大括号跟随语句放行末,前置1个空格
...
} else { // 右大括号、"else"、以及后续的左大括号均在同一行
...
} // 右大括号独占一行
...
}
- 条件、循环语句使用大括号,比如:
if (objectIsNotExist) { // 单行条件语句也加大括号
return CreateNewObject();
}
while (condition) {} // 即使循环体是空,也应使用大括号
while (condition) {
continue; // continue表示空逻辑,使用大括号
}
case/default语句相对switch缩进一层,缩进风格如下:
switch (var) {
case 0: // 缩进一层
DoSomething1(); // 缩进一层
break;
case 1:
DoSomething2();
break;
default:
break;
}
- 一行只写一条语句。
- 一条语句不能过长,建议不超过120个字符,如不能缩短语句则需要分行写。
换行时将操作符留在行末,新行进行同类对齐或缩进一层,比如:
// 假设下面第一行不满足行宽要求
if (currentValue > MIN && // 换行后,布尔操作符放在行末
currentValue < MAX) { // 与(&&)操作符的两个操作数同类对齐
DoSomething();
...
}
// 假设下面的函数调用不满足行宽要求,需要换行
ReturnType result = FunctionName(paramName1,
paramName2,
paramName3); // 保持与上方参数对齐
ReturnType result = VeryVeryVeryLongFunctionName( // 写入第1个参数后导致过长,直接换行
paramName1, paramName2, paramName3); // 换行后,4空格缩进一层
// 每行的参数代表一组相关性较强的数据结构,放在一行便于理解,此时可理解性优先于格式排版要求
int result = DealWithStructLikeParams(left.x, left.y, // 表示一组相关参数
right.x, right.y); // 表示另外一组相关参数
- 声明定义函数时,函数的返回类型以及其他修饰符,与函数名同行。
指针类型"*"应该靠右跟随变量或者函数名,比如:
int *p1; // Good:右跟随变量,和左边的类型隔了1个空格
int* p2; // Bad:左跟随类型
int*p3; // Bad:两边都没空格
int * p4; // Bad:两边都有空格
- 当"*"与变量或函数名之间有其他修饰符,无法跟随时,此时也不要跟随修饰符,比如:
char * const VERSION = "V100"; // Good:当有const修饰符时,"*"两边都有空格
int Foo(const char * restrict p); // Good:当有restrict修饰符时,"*"两边都有空格
- 根据上下内容的相关程度,合理安排空行,但不要使用连续3个或更多空行。
编译预处理的"#"统一放在行首,无需缩进。嵌套编译预处理语句时,"#"可以进行缩进,比如:
#if defined(__x86_64__) && defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_16) // 位于行首,不缩进
#define ATOMIC_X86_HAS_CMPXCHG16B 1 // 缩进一层,区分层次,便于阅读
#else
#define ATOMIC_X86_HAS_CMPXCHG16B 0
#endif
注释
- 注释的内容要清楚、明了,含义准确,防止注释二义性。
- 在代码的功能、意图层次上进行注释,即注释解释代码难以直接表达的意图,而不是仅仅重复描述代码。
- 函数声明处注释描述函数功能、性能及用法,包括输入和输出参数、函数返回值、可重入的要求等;定义处详细描述函数功能和实现要点,如实现的简要步骤、实现的理由、设计约束等。
- 全局变量要有较详细的注释,包括对其功能、取值范围以及存取时注意事项等的说明。
- 避免在注释中使用缩写,除非是业界通用或子系统内标准化的缩写。
- 文件头部要进行注释,建议注释列出:版权说明、版本号、生成日期、作者姓名、功能说明、与其它文件的关系、修改日志等。
- 注释风格要统一,建议优先选择/* */的方式,注释符与注释内容之间要有1空格,单行、多行注释风格如下:
/* 单行注释 */
/*
* 多行注释
* 第二行
*/
#define A_CONST 100 // 此处两行注释属于同类
#define ANOTHER_CONST 200 // 可保持左侧对齐
宏
- 代码片段使用宏隔离时,统一通过#ifdef的方式,例如:
#ifdef LOSCFG_XXX
...
#endif
- 定义宏时,要使用完备的括号,比如:
#define SUM(a, b) a + b // 不符合本条要求
#define SUM(a, b) ((a) + (b)) // 符合本条要求
但是也要避免滥用括号,比如单独的数字或标识符加括号毫无意义:
#define SOME_CONST 100 // 单独的数字无需括号
#define ANOTHER_CONST (-1) // 负数需要使用括号
#define THE_CONST SOME_CONST // 单独的标识符无需括号
- 包含多条语句的函数式宏的实现语句必须放在do-while(0)中,例如:
#define FOO(x) do { \
(void)printf("arg is %d\n", (x)); \
DoSomething((x)); \
} while (0)
- 禁止宏调用参数中出现预编译指令。
- 宏定义不以分号结尾。
头文件
- 设计原则
- 头文件应当职责单一。
- 一个模块通常包含多个.c文件,建议放在同一个目录下,目录名即为模块名;如果一个模块包含多个子模块,则建议每一个子模块提供一个对外的.h,文件名为子模块名。
- 建议每一个.c文件应有一个同名.h文件,用于声明需要对外公开的接口。
- 头文件中适合放置接口的声明,不适合放置实现。
- 不要在头文件中定义变量。
- 禁止头文件循环依赖,循环依赖指a.h包含b.h,b.h包含c.h,c.h包含a.h。
- 头文件应当自包含,即任意一个头文件均可独立编译,但同时也要避免包含用不到的头文件。
- 头文件必须用#define保护,防止重复包含,比如内核中统一使用以下宏定义保护:
#ifndef _LOS_<MODULE>_H // 比如 _LOS_TASK_H #define _LOS_<MODULE>_H ... #endif
- 禁止通过声明的方式引用外部函数接口、变量,只能通过包含头文件的方式使用其他模块或文件提供的接口。
- 禁止在 extern "C" 中包含头文件。
- 按照合理的顺序包含头文件:
- 源文件对应的头文件
- C标准库
- 需要包含的OS其他头文件
- 版权声明
- 头文件版权声明一致,放在头文件置顶位置。
- 如果提交的代码是在开源软件基础上修改所编写或衍生的代码,请遵循开源许可协议要求,并且已履行被修改软件的许可证义务。
数据类型
基础类型定义统一使用los_typedef.h中定义的类型,比如定义无符号32位整型变量使用UINT32。
变量
- 一个变量只有一个功能,不要把一个变量用作多种用途。
- 防止局部变量与全局变量同名。
- 不用或者少用全局变量。
- 定义函数的局部变量时,控制变量的占用空间,避免因占用过多栈空间导致程序运行失败。比如需要一个大数组,可以通过动态分配内存的方式来避免栈空间占用过大。
- 在首次使用前初始化变量。
- 指向资源句柄或描述符的变量,在资源释放后立即赋予新值,包括指针、socket描述符、文件描述符以及其它指向资源的变量。
- 禁止将局部变量的地址返回到其作用域以外,下面是一个错误示例:
int *Func(void) { int localVar = 0; ... return &localVar; // 错误 } void Caller(void) { int *p = Func(); ... int x = *p; // 程序产生未定义行为 }
正确代码示例:
int Func(void) { int localVar = 0; ... return localVar; } void Caller(void) { int x = Func(); ... }
- 如果要使用其他模块的变量,应尽量避免直接对变量进行访问,而是通过统一的函数封装或者宏封装的方式,比如mutex模块中:
// 私有头文件中引入全局变量,但要避免直接使用 extern LosMuxCB *g_allMux; // 通过GET_MUX的方式对g_allMux进行访问 #define GET_MUX(muxID) (((LosMuxCB *)g_allMux) + GET_MUX_INDEX(muxID))
函数
- 重复代码应该尽可能提炼成函数。
- 避免函数过长,新增函数不超过 40-50 行。
- 内联函数要尽可能短,避免超过 10 行(非空非注释)。
- 避免函数的代码块嵌套过深。
- 函数应避免使用全局变量、静态局部变量和I/O操作,不可避免的地方应集中使用。
可移植性
不使用与硬件或操作系统关系很大的语句,而使用建议的标准语句,以提高软件的可移植性和可重用性。
C语言编程规范参考资料较多,大家可以自行了解,本文不再过多赘述。
参考
[1]. 华为LiteOS编程规范
边栏推荐
猜你喜欢
第十三单元 混入视图基类
第八单元 中间件
未来的金融服务永远不会停歇,牛市仍将继续 2021-05-28
政策利空对行情没有长期影响,牛市仍将继续 2021-05-19
Interview | with questions to learn, Apache DolphinScheduler Wang Fuzheng
第十四单元 视图集及路由
Sentinel源码(四)(滑动窗口流量统计)
第十五单元 分页、过滤
Break the limit of file locks and use storage power to help enterprises grow new momentum
瑞吉外卖笔记——第08讲读写分离
随机推荐
世界上最大的开源基金会 Apache 是如何运作的?
【Tensorflow】AttributeError: ‘_TfDeviceCaptureOp‘ object has no attribute ‘_set_device_from_string‘
专访|带着问题去学习,Apache DolphinScheduler 王福政
无序数组排序并得到最大间隔
Flask请求应用上下文源码分析
如何解决1045无法登录mysql服务器
海明校验码纠错设计原理
What are the file encryption software?Keep your files safe
deal!It's July 30th!
Supervision strikes again, what about the market outlook?2021-05-22
drf路由组件Routers
关于密码加密的一点思路
线代:已知一个特征向量快速求另外两个与之正交的特征向量
MySQL数据库语法格式
关于市场后市的发展预测? 2021-05-23
一维卷积神经网络_卷积神经网络的基础知识「建议收藏」
Minio文件上传
史上最全!47个“数字化转型”常见术语合集,看完秒懂~
Sentinel源码(四)(滑动窗口流量统计)
[ROS](02)创建&编译ROS软件包Package