静态代码和动态代码的区别_静态代码,我想静静(上)

3375713c5767c80e6316bf011c1663b1.png

作者:莫毅 | 编辑:萌萌

静态代码,本期的主角,静静同学,可不像它的名字一样安安静静,而是暗藏玄机,一不小心就会引发风起云涌。静静轻轻一扇翅膀,也许就会引发山崩地坼。

所以,作为一名合格的QA,为了更好的保障我们的产品,不能总是把静静放在黑盒测试的小黑屋里听之任之,而是要慢慢了解她,深入认识她,不放过任何一串可疑代码,从源头发现和解决质量问题。

21682de41c41259b4d182358fb9f09eb.png 初识静态代码扫描

静态代码扫描,学名:静态程序分析(Static program analysis)是指在不运行计算机程序的条件下,进行程序分析的方法。绝大部分的静态程序分析的对象是针对特定版本的源代码,也有些静态程序分析的对象是目标代码。其主要特点如下:

【优点】

  1. 不需要编译通过代码,仅通过扫描代码发现问题,可以跳过不同语言严苛或复杂的编译过程,更迅捷的发现问题;

  2. 通过规则自动化扫描代码,便可以直接检测出常见逻辑错误,空指针,内存泄漏等常见问题,问题覆盖面广,代码覆盖率高;

  3. 降低人工review代码的工时成本,自动化的发现bug和潜在问题。

【缺点】

  1. 只能发现naive的逻辑错误,而不能发现业务相关的逻辑问题,仍然不能彻底解放人工Code Review;

  2. 扫描规则过细会导致扫描出的非高优问题过多而增加不必要的工作量,但是此问题可以通过问题分级或问题过滤等手段进行规避和改进。

21682de41c41259b4d182358fb9f09eb.png 静态扫描规则&实例

静态代码扫描的一般规则包含以下八个种类:

- 空指针检查(Null Pointer)

- 数据越界(Buf Over Run)

- 内存泄漏(Mem Leak)

- 逻辑错误(Logic)

- 可疑代码检查(Suspicious)

- 运算错误(Compute)

- 高危函数(Unsafe Func)

- 未初始化(Uninit)

每个分类的具体规则详细而繁多,接下来小编来列举一些在实际工程中碰到的静态代码的经典错误。

21682de41c41259b4d182358fb9f09eb.png 空指针检查 - 先判空后接引用

指针先判空,然后在判空作用域外接引用。如代码所示,前一个if出现了变量A是否存在的判断,故认为变量A不一定存在,而在后文紧接着的下一个并列的判断中,直接使用了该变量未加任何判空保护,所以一旦变量A为空的情况,必然会出现空指针错误。

int FuncA::Init(const Config& config, std:: classParamA, std::classParamB) {

    ......

    if (VarA) {

        VarA->Init(ObjectA);

        VarA->FuncB(params);

    }

    if (Condition....) {

        ......

        VarA->FuncC(params);

    }

    return 0;

}

21682de41c41259b4d182358fb9f09eb.png 逻辑错误 - 无效的逻辑判断

从代码段可以看出,这一个if判断包含了两个条件:变量B≥0,变量B≤255,而这两个条件通过或来连接会导致整个条件包含了全部实数,无论变量B为多少,该判定永远为真。

27bc6e13e2f2cab081c018410ba1666f.png

void FuncA(params) {

    ......

    if (VarB >= 0 || VarB <= 255) {

       funcB(VarB);

    }

    ......

}

21682de41c41259b4d182358fb9f09eb.png 未初始化 - 不完整的初始化

构造函数中部分成员变量未初始化,在如下的代码示例段中,变量A,B并未进行初始化就直接进行赋值和调用。虽然看起来是非常低级的错误,但是在庞大的工程中,一旦出现,想要凭借人工review发现,也是难上加难。

ClassC(ClassA VarA, ClassB VarB)

        :VarC(C) {

    mVarA = VarA;

    ......

    mVarB = VarB;

    ......

}

21682de41c41259b4d182358fb9f09eb.png 逻辑错误 - 可能的表达式溢出

在不同编译环境下,unsigned long数据类型与int数据类型的字节长度会改变,如下表所示:

编译器位数

int

unsigned long

16位

2字节

4字节

32位

4字节

4字节

64位

4字节

8字节

因此不难看出,以下代码段的运算结果在不同的编译环境下有超出位数的风险,建议先做类型提升,再进行运算。

void FuncA(int w, int h) {

    ......

    int varA1 = X.width();

    int varB1 = X.height();

    int varA2 = Y.width();

    int varB2 = Y.height();

    const unsigned long varC = varA1 * varA2;

    const unsigned long varD = varB1 * varB2;

    ......

}

本期,小编带大家认识了静态代码,以及静态代码扫描的一般规则和实例,相信大家对于静静同学已经不再陌生。

接下来,静态代码扫描在实际工程中如何应用,目前市面有哪些好用的静态代码扫描工具,如何选择适合自己的静态代码扫描工具呢,请听下回分解。

5a075dab554c93b2805c96b3443f4a34.png

83372e66c080984aaf00553efe60dc36.png


版权声明:本文为weixin_39717318原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。