PCLINT(1) LINT 代码规范
1. LINT 注意事项
注意1:LINT的价值在于能够引导和辅助我们挖掘代码中的隐患对代码进行优化和改进分析,以提高我们的水平。
反之,机械地物理上消除LINT告警是一种舍本逐末的行为。
注意2:消除LINT告警本身不是最终目的,LINT检查的关键价值在于引导我们分析和消除产生告警的问题根源。
注意3:通过关闭选项来消除告警对提升代码质量无任何效果,如果时间和精力有限,可以逐步分析和消除。
尽量不要大批量修改告警,由于涉及众多文件,极意修改错误。
注意4:告警的级别和问题的严重程度没有简单的对应关系,即便是Info 级都有可能是致命问题。
2. LINT 代码规范
-
不要使用难懂的技巧很高的语句 禁止使用三目运算符 ?:,简单明了即可。
-
内存分配和释放需要在同一层级,空指针释放也需要在同一个层级。
- 要求:函数的内存规则简化:即任何情况下,函数都统一做释放(或不释放)内存处理
- 避免指针内存重复被释放
- 避免使用空指针
- 避免释放后再使用
- 避免内存越界访问
尤其是数组指针和指针数组 - 避免访问某些未初始化的指针结构
- 注意内存的异常处理
- 注意内存申请失败处理
- 注意链表上的野指针处理,及内存释放
-
避免缓冲区越界访问,如:字符串数组copy越界
处理:不要无缘无故扩大缓冲区,治标不治本
无拷贝需求时,禁止随意拷贝字符串 -
避免遗忘对接口句柄的销毁处理,如:接口句柄被忘记释放、销毁
改进:避免随便退出控制结构,保持最少的,最好唯一的返回点 -
避免设计复杂的代码结构(众多交错的 if else;for;switch;)
- if,switch case 分支建议用来处理真正的差别,保持简洁短小,而不是大断类似代码
- if else 对成检查,不要缺少else分支
- 不要在if while for switch 等控制逻辑的条件表达式中调用函数
-
输入参数由调用者保证,比如模块内部
-
禁止断言作为错误处理;注意:断言只在Debug 版本有效,release无效
-
控制结构后需要大括号,哪怕只有一行
建议:不要在if while for switch 等控制逻辑的条件表达式中调用函数 -
不允许将sizeof运算符作用于有副作用的表达式上
例如:
ulLen = sizeof ( g_ulType = 0x1234)
sizeof运算符只能针对数据类型进行操作,所有下述代码实际被替换成了
ulLen = sizeof ( unsigned long)
因此 g_ulType = 0x1234 不会执行, -
逻辑运算符 && || ! 的操作数必须为一个有效的布尔值,布尔值表达式
不允许进行逻辑运算以外的操作。为了防止误用。
如: if(!pTemp) 这里应该是bool 类型的变量 -
移位数据类型缺陷预防处理
char ch=0x5a;
char result;
result = (~ch)>>4; 期望 ch取反右移4位 结果为 0x0A
但是实际:32位编译器中,ch取反 0xFFFFFFA5 右移4位后,赋值给re有一个截断操作 result=0xFA
正确: result= ((char)(~ch)) >> 4; 取反为0xA5,右移4位, 为 0x0A
注:如果移位操作用于char类型等整形数据时,应该显示强制转换为预期类型 -
路径中反斜杠错误检查
#include “…/def.h” ,避免使用 \ -
推荐使用十六进制或十进制,尽量少用8进制
因为: #define NUM 012
容易和十进制混淆,如:012和12,前者取值10 ,后者为12 -
函数不带参数应该显示明确表示不带参数VOID
如: VOID FUN(VOID){…………} -
拼接字符串时,防止越界缓冲区,建议使用 OS_nsprintf,以确保不越界
-
不要进行多余的强制类型转换,否则可能会影响pclint效果
-
对于既要传递指针又要传递数值的函数参数设计为 VOID *,而不是 ULONG
-
定义宏时,要将整个宏表达式用括号包含
例如: 求最大值的宏
#define MAX(X,Y) ((X>Y) ? (X):(Y)) -
避免在成对的系统资源操作之间产生异常退出分支,特别是资源申请释放时
信号量PV操作,中断开关操作等。对称操作应该尽可能最小化,
严禁将与对称操作无关的功能或逻辑在对称操作区间中实现。
信号量操作之遗漏解锁 -
数据结构设计时应该考虑字段对齐,以节省内存空间
-
避免使用默认优先级,用括号明确表达式的运算顺序
-
不允许使用逗号表达式
逗号表达式定义: 表达式1,表达式2,……
从左往右逐个计算
如:
int x,y,z;
x=1,y=1;
z=(x++,y++,++y)
结果: x y z = 1 3 3 -
应该仅在所有操作成功后,再将资源/结果赋给相应的数据结构或者全局变量
-
函数如果输出资源,建议通过函数返回值来传递,而不是输出参数
-
申请资源时先赋值给临时指针变量,待检查无误,保证所有操作成功后,再赋值或挂接给数据结构
例如:
错误用法: pResult->pA = malloc(xx)
正确用法:pTmp = malloc(xx)
if(NULL == pTmp)
{…………} //检查处理等操作
pResult->pA = pTmp -
应该间接释放数据结构字段的资源
错误用法:free(pResult->pA)
正确用法:pTmp = pResult->pA
free(pTmp)
因为:所有操作资源的地方尽量使用局部变量,以保证LINT检测功能
发挥作用,同时也在一定程度上有利于代码优化。 -
缓冲区的长度等关键信息作为输入参数,不要和输出类信息混合。
因为:以简化逻辑和提高代码的可LINT性。 -
const使用
- 对于固定或值为常量的全局变量建议使用const或使用宏定义,以提高LINT性。
- 函数的参数如果只为了只读属性,特别是只读的字符串参数,建议使用const修饰,以加强语法检查,避免被误用。
-
数据源不可靠时,字符串拷贝类操作必须使用有长度限制的 OS_strncpy 系列函数
数据源不可靠时,字符串拼装类操作必须使用 OS_nsprintf 以确保缓冲区不会越界 -
最后,函数功能设计应遵循功能单一、逻辑简单、结构清晰原则。