AFUIOT C语言编程风格技术干货分享

时间:2020年7月18日   作者:安服优

1.背景

本文档是安服优物联网开放平台C语言编程的规范文件,AFUIOT开放平台项目的所有C语言代码均采用此规范编写,本规范基于谷歌开源项目C++风格指南改编,与Google C++ Style Guide兼容,可视为其C 语言的子集,本文档列出了C语言代码风格的常用要求,使用AFUIOT开放平台的C语言代码需遵循此文档的要求,同时仍可详细查阅Google C++ Style Guide。 本文档包含于AFUIOT开放平台equalOS内,并包含有文件模版以及使用本规范编写的.c与.h文件示例,Google C++ Style Guide的中英文版本可查询以下链接: https://zh-google-styleguide.readthedocs.io/en/latest/google-cpp-styleguide/ https://github.com/google/styleguide

2. 头文件

2.1 通常每一个.c文件都需要有对应的.h文件; 2.2 使用define保护头文件,格式为\ <PROJECT>_\ <FILE>_H_,如 : #define EQUALOS_INT_H_ 2.3 兼容C/C++的混合编程,使用下列宏来实现: 2.4 头文件应该自给自足,即包含此文件时不需要再含额外的头文件来保证编译正确; 2.5 应该使用include包含头文件,不要在.c文件里extern引入函数或者全局变量; 2.6 不要在头文件中定义普通函数,如果定义内联函数则内联函数通常不要超过10行; 2.7 避免没有使用的include,只include必要的,include的顺序为: 1)C系统头文件 2)其它库的头文件 3)项目头文件

3. 作用域

3.1 遵循尽可能小的作用域原则,减少接口数量,如一个全局变量只在单个函数内使用就在这个函数内用static定义成局部静态变量,如只在单个.c文件内使用则在此文件中定义成全局静态变量,如函数只在一个.c文件中使用则把此函数用static定义成局部函数; 3.2 函数内的局部变量需定义在函数开头,不能定义在语句中。

4. 函数

4.1 参数顺序 函数的参数顺序为: 输入参数在先, 后跟输出参数。 说明: C/C++ 中的函数参数或者是函数的输入, 或者是函数的输出, 或兼而有之. 输入参数通常是值参或 const 引用, 输出参数或输入/输出参数则一般为非 const 指针. 在排列参数顺序时, 将所有的输入参数置于输出参数之前. 特别要注意, 在加入新参数时不要因为它们是新参数就置于参数列表最后, 而是仍然要按照前述的规则, 即将新的输入参数也置于输出参数之前。 4.2 声明与定义函数时一定要定义参数名,不要只定义类型,如: 4.3 编写简短的函数,通常一个函数不要超过40行,但如果函数内部是紧密关联的,且无需拆分成不同的模块供调用,则可以编写较长的函数。

5. 命名约定

最重要的一致性规则是命名管理. 命名的风格能让我们在不需要去查找类型声明的条件下快速地了解某个名字代表的含义: 类型, 变量, 函数, 常量, 宏, 等等, 甚至. 我们大脑中的模式匹配引擎非常依赖这些命名规则。 命名规则具有一定随意性, 但相比按个人喜好命名, 一致性更重要, 所以无论你认为它们是否重要, 规则总归是规则,即要严格遵守规则。

5.1 通用命名规则

函数命名, 变量命名, 文件命名要有描述性; 少用缩写。 尽可能使用描述性的命名, 别心疼空间, 毕竟相比之下让代码易于新读者理解更重要. 不要用只有项目开发者能理解的缩写, 也不要通过砍掉几个字母来缩写单词. 注意, 一些特定的广为人知的缩写是允许的, 例如用 i 表示迭代变量和用 T表示模板参数. 5.2 文件命名 文件名要全部小写, 可以包含下划线 (_) 或连字符 (-), 依照项目的约定. 如果没有约定, 那么 “_” 更好. 通常应尽量让文件名更加明确. http_server_logs.h 就比 logs.h 要好. 内联函数必须放在 .h 文件中. 如果内联函数比较短, 就直接放在 .h 中.

5.3 类型命名

每个单词首字母均大写,不包含下划线,不同的类型以不同的尾辍结束; 结构体类型以Def结尾: 函数指针以Fun结尾: typedef void (* ExampleFun) (void*) ; 枚举类型以Enum结尾: 除此之外需减少重定义类型的数量,尽量使用标准类型,优先使用uint8_t一类有符号与位宽的类型;

5.4 变量命名

全部使用小写,以下划线连接,包括普通变量、结构体成员、函数参数,在此基础上: 全局变量:以g_为前辍; 静态变量:以static定义的全局静态变量及局部静态变量以gs_为前辍; uint32_t g_systick_count = 0; uint32_t gs_my_index = 0;

5.4 常量命名

大小写混合,单词首字母需大写,并以k开头,如:const int kDaysInAWeek = 7;

5.6 函数命名

以大小写混合,单词首字母需大写; 一般来说, 函数名的每个单词首字母大写 (即 “驼峰变量名” 或 “帕斯卡变量名”), 没有下划线. 对于首字母缩写的单词, 更倾向于将它们视作一个单词进行首字母大写 (例如, 写作 StartRpc() 而非 StartRPC()).

5.7 枚举命名

枚举同常量一样;

5.8 宏命令

全部大写,以下划线连接;

5.9 命名分类表

6. 注释

6.1 注释风格

大括号外使用/*/,大括号内可使用//注释,/ 与// 后总是有一个空格;

6.2 文件注释

每一个文件开头应该有版权公告,文件内需划分板块,不同的代码写入对应的板块中,文件注释与板块划分可参考文件模版;

6.3 函数注释

使用javadoc或doxygen格式为函数声明处前加上注释; 基本上每个函数声明处前都应当加上注释, 描述函数的功能和用途. 只有在函数的功能简单而明显时才能省略这些注释(例如, 简单的取值和设值函数). 注释使用叙述式 (“Opens the file”) 而非指令式 (“Open the file”); 注释只是为了描述函数, 而不是命令函数做什么. 通常, 注释不会描述函数如何工作. 那是函数定义部分的事情. 函数声明处注释的内容: 函数的输入输出. 使用该函数必须要处理的后续工作 是否存在函数使用上的性能隐患. 在函数定义内需要注释的情况: 如果函数的实现过程中用到了很巧妙的方式, 那么应当加上解释性的注释; 避免描述不清楚,或者与代码不相干或者完全相反的注释,除此以外注释还是越多越好。

6.4 变量注释

一般情况下,变量名就是足够的注释,但是全局变量与常量需要添加详细的注释,特别是在变量需要特殊的赋值或调用方法时;

6.5 TODO注释

临时或未完成的工作需使用大写TODO注释,在随后的圆括号里写上你的名字

7. 格式

7.1 行长度

每一行代码的长度不要超过80,;

7.2 编码

使用UTF-8 no bom编码,如果引入的代码是非UTF-8 no bom的,先使用工具转换格式后再编辑;

7.3 只使用空格

每次缩进2个空格,将编辑器的制表符设置成转换为空格;

7.4 函数的声明与定义

返回类型与函数名放在同一行,合适的话参数也放在同一行,如果一行放不下,则参数可以与第一参数对齐; 函数看上去像这样: 如果同一行文本太多, 放不下所有参数: 甚至连第一个参数都放不下:

7.5 函数调用

函数调用遵循如下形式: 如果同一行放不下, 可断为多行, 后面每一行都和第一个实参对齐, 左圆括号后和右圆括号前不要留空格: 参数也可以放在次行, 缩进四格:

7.6 其它格式说明

不要在圆括号中添加空格; 空循环体应使用{}或continue,而不是一个简单的分号; 函数返回值不要使用圆括号; 预处理指令不要缩进; 水平留白,水平留白的使用根据在代码中的位置决定. 永远不要在行尾添加没意义的留白; 垂直留白,垂直留白越少越好,不到万不得已,不要使用空行;

7.7 美化

针对代码的格式,谷歌C++风格指南还有更详细的要求,如果程序员完全依靠手动输入很难达到全部的要求,记忆这么复杂的格式也不轻松,好消息是如果使用一款比较好的编辑器,一般会有美化功能,可以一键生成符合格式要求的化码,以下是支持Google Coding Style的编辑器推荐: SlickEdit Visual Studio Code Eclipse 当你修改使用其他风格的代码时, 为了与代码原有风格保持一致可以不使用本指南约定. 如果不放心, 可以与代码原作者或现在的负责人员商讨. 记住:一致性也包括原有的一致性。