代码中%80的非逻辑性代码都可以被它发现
前言
很多代码问题在编译阶段难以发现,只有在运行时才会暴露。即便是在运行时出现问题了,我们可能仍然需要费一番功夫才能最终找到代码的问题。幸运地是,我们可以利用一个工具在编译之前就可以发现这些问题。有了它,基本可以检查出代码中80%的非逻辑性错误。这就是本文要介绍的主角--PC-lint。
PC-lint简介
PC-Lint 是GIMPEL SOFTWARE公司开发的C/C++软件代码静态分析工具。而所谓静态分析是指在不运行代码的方式下,通过词法分析、语法分析、控制流、数据流分析等技术对程序代码进行扫描,验证代码是否满足规范性、安全性、可靠性、可维护性等指标--摘自百科。也就是说,利用PC-lint对我们的代码进行扫描分析,在程序运行之前,就可以发现代码中隐藏的问题。PC-lint除了能够发现诸如未初始化变量、数组越界、内存泄漏等问题,还能提出许多对程序运行效率,空间等方面的改进点。下面就简单介绍一下如何使用PC-lint。
如何使用PC-lint
PC-lint能够在Windows、MS-DOS和OS/2平台上使用,Linux平台可使用FlexeLint、Splint等替代工具。本文介绍仅PC-lint的使用。
注:PC-lint为商用软件。
安装方法不在此介绍,和其他普通软件的安装方式一样。安装完成后,在安装目录下会有lint-nt.exe程序。基本使用方法如下:
1lint-nt.exe -u files.lnt #执行之后扫描结果会显示在控制台
其中files.lnt文件中的内容是需要扫描的源代码位置。
例如files.lnt文件内容如下:
1D:\pclint\lint\test\test.c
2D:\pclint\lint\test\main.c
表明将会对main.c和test.c进行静态检查。
如果源文件比较多,那么将源文件添加带files.lnt中是一件很繁琐的事情,我们可以使用命令来得到我们的files.lnt文件:
1dir /S/B *.h *.c > files.lnt
示例程序
我们直接来看一个例子,看看PC-lint到底有哪些能耐。
示例代码
1/*main.c*/
2
3int main(void)
4{
5
6 int a[] = {1,2,3,4,5};
7 int sum;
8 unsigned int len = sizeof(a)/sizeof(int);
9 int loop;
10 for(loop = 0;loop <= len;loop++)
11 {
12 sum += a[loop];
13 }
14 if(15 == sum)
15 {
16 printf("sum = 15\n");
17 return 0;
18 }
19 else
20 {
21 printf("sum != 15,sum=%d\n",sum);
22 return -1;
23 }
24}
上面的代码计算数组a的和,并且判断最后和是否等于15。
lnt配置
我们的lnt文件files.lnt配置如下:
1-wlib(0) //对库文件不输出任何错误信息
2-iD:\pclint\include //指定头文件路径
3D:\pclint\lint\test\main.c //我们的源代码文件
由于我们的代码包含了stdio.h头文件,因此还需要stdio.h头文件,我把它放在了D:\pclint\include,并在lnt文件中指定了头文件的位置。另外,我们只需要扫描我们自己的源代码,因此使用了-wlib(0)来避免对库文件输出告警信息。
扫描代码
执行命令:
1D:\pclint\lint>lint-nt.exe -u .\test\files.lnt>result.txt
这里我们将结果重定向到了result.txt文件中,最后生成的result.txt内容如下:
1--- Module: D:\pclint\lint\test\main.c (C)
2 _
3 for(loop = 0;loop <= len;loop++)
4D:\pclint\lint\test\main.c 10 Warning 574: Signed-unsigned mix with
5 relational
6D:\pclint\lint\test\main.c 10 Info 737: Loss of sign in promotion from int to
7 unsigned int
8 _
9 sum += a[loop];
10D:\pclint\lint\test\main.c 12 Warning 530: Symbol 'sum' (line 7) not
11 initialized
12D:\pclint\lint\test\main.c 7 Info 830: Location cited in prior message
13 _
14 sum += a[loop];
15D:\pclint\lint\test\main.c 12 Warning 661: Possible access of out-of-bounds
16 pointer (1 beyond end of data) by operator '[' [Reference: file
17 D:\pclint\lint\test\main.c: lines 8, 10, 12]
18D:\pclint\lint\test\main.c 8 Info 831: Reference cited in prior message
19D:\pclint\lint\test\main.c 10 Info 831: Reference cited in prior message
20D:\pclint\lint\test\main.c 12 Info 831: Reference cited in prior message
21 _
22 printf("sum = 15\n");
23D:\pclint\lint\test\main.c 16 Warning 534: Ignoring return value of function
24 'printf(const char *, ...)' (compare with line 271, file
25 D:\pclint\include\stdio.h)
26D:\pclint\include\stdio.h 271 Info 830: Location cited in prior message
27 _
28 printf("sum != 15,sum=%d\n",sum);
29D:\pclint\lint\test\main.c 21 Warning 534: Ignoring return value of function
30 'printf(const char *, ...)' (compare with line 271, file
31 D:\pclint\include\stdio.h)
32D:\pclint\include\stdio.h 271 Info 830: Location cited in prior message
问题分析
经过扫描之后,发现了代码中的很多问题。我们一一列举:
第10行警告号574,提示有符号数和无符号数混用。我们确实将有符号数loop和无符号数len进行了比较。
第12行警告号530,sum未进行初始化。定义sum变量时,并未进行初始化。
第12行警告号661,提示可能出现数组越界。我们仔细审查代码就会发现,循环对a进行求值时,其循环条件应该是loop < len而不是loop <= len。
第16行,21行提示有返回值没有使用。我们调用printf函数之后,并没有必要使用其返回值,因此我们可以忽略这个警告。
第24行提示警告号527,return语句不可到达。由于前面的if-else结构,使得最后的return语句永远无法执行。
问题修改
前面这段代码是可以编译通过,并且运行的,但是经过PC-lint扫描之后却发现如此之多的问题。我们将发现的问题代码进行修改后如下:
1/*main.c*/
2
3/*lint -e{534}*/
4int main(void)
5{
6
7 int a[] = {1,2,3,4,5};
8 int sum = 0;
9 unsigned int len = sizeof(a)/sizeof(int);
10 unsigned int loop;
11 for(loop = 0;loop < len;loop++)
12 {
13 sum += a[loop];
14 }
15 if(15 == sum)
16 {
17 printf("sum = 15\n");
18 return 0;
19 }
20 else
21 {
22 printf("sum != 15,sum=%d\n",sum);
23 return -1;
24 }
25}
最终PC-lint检查结果如下:
1--- Module: D:\pclint\lint\test\main.c (C)
这里除了修改了我们确定的问题之外,还屏蔽了PC-lint的534号警告,因为我们确认这不会对我们的程序本意造成任何影响,因此使用/*lint -e{534}*/屏蔽了main函数的534号警告。PC-lint屏蔽警告的方法很多,这里不再详述。
总结
通过示例程序可以看出,PC-lint确实能够发现一些隐藏的问题,但实际上它的强大远不止我们前面所看到的那样,利用好PC-lint能够帮助我们在运行程序之前就发现很多难以察觉的问题。本文本意为介绍PC-lint的用途,因此对PC-lint的详细使用并没有做过多介绍,有兴趣的读者可以参考网上的资料进行配置学习,PC-lint所报的警告号都可以通过官方PC-lint错误码查看其含义,帮助修正我们的程序。
问题思考
最原始的代码,运行结果是什么?为什么会出现这样的结果?
如果将sum定义为全局静态变量,并且将循环条件改为loop < len,还会出现同样的结果吗?为什么?
欢迎在留言区评论留言。
推荐阅读: