使用cppcheck检测代码警告、错误

cppcheck是一个C/C++静态检查工具。它可以帮助我们检测出代码存在(潜在)的问题,比如数组越界、内存申请未释放、文件打开未关闭。注意,cppcheck不是编译器,替代不了gcc。

技术小结

使用小结:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
使用:
cppcheck <工程目录> <选项> 2>&1 | tee <输出文件>

默认:
cppcheck ./

输出信息到终端及文件:
cppcheck ./ 2>&1 | tee check.txt

输出到xml文件(好像浏览器不能很好解析得到的xml)
--xml --xml-version=2 . 2>&1 | tee check.xml

选项

所有:
--enable=all

过滤src/xml目录
--suppress='*:src/xml/*'

忽略一些警告:
--suppress=missingIncludeSystem --suppress=variableScope --suppress=ConfigurationNotChecked --suppress=unusedFunction
(其中missingIncludeSystem等可在输出结果中查看)


一些示例命令:

1
./ --enable=all --suppress='*:src/xml/*' --suppress=variableScope  2>&1 | tee check.txt

安装

在ubuntu下安装cppcheck十分简单,命令如下:

1
root@latelee:latelee# apt-get install cppcheck 

测试

下面代码片段包含了数组越界、内存申请未释放、文件打开未关闭等错误。由于是演示用,不必太在意代码细节。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

void init_buffer(void)
{
char filename[128] = {""}; // 这样初始为0,如果是{"1"},则只有第1个字节为1,其它为0 --不知其它编译器会怎样

printf("test of buffer\n");

dump(filename, 128);

char unused_buffer[7*1024*1024] = {0}; // 没有使用的缓冲区,超过栈最大值,有coredump。
char unused_buffer1[1*1024*1024] = {0};

strcpy(unused_buffer1, "hello");
}

// 数组范围越界
void out_of_array(void)
{
int foo[2]; // 范围不够
int aaa = 250;

foo[0] = 1;
foo[1] = 2;
// 下面这些是不行的
foo[2] = 3;
foo[3] = 4;
foo[4] = 5;
foo[5] = 6;

printf("%d %d \n", foo[0], foo[1]);
}

#include <sys/types.h>
#include <dirent.h>
// 打开未关闭
void open_not_close()
{
// 内存泄漏
char* p = new char[100];
strcpy(p, "hello");
printf("p:%s\n", p);


FILE* fp = NULL;
fp = fopen("aaa", "a");

if (fp)
{
// 注:这里返回时没有关闭文件
return;
}

fclose(fp);


DIR *dir = NULL;
dir = opendir("./");

}

int main(void)
{
int ret = 0;

foo();

init_buffer();
out_of_array();
open_not_close()
return 0;
}

注意,在open_not_close函数中的return前并没有关闭fp文件指针。这种错误特别容易发生,幸运的是,cppcheck可以检查出来。下面是是检测结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
root@latelee~/test# cppcheck gcc_warning.cpp  --enable=all
Checking gcc_warning.cpp...
[gcc_warning.cpp:56] -> [gcc_warning.cpp:50]: (warning) Possible null pointer dereference: fp - otherwise it is redundant to check it against null.
[gcc_warning.cpp:13]: (style) Variable 'unused_buffer' is assigned a value that is never used.
[gcc_warning.cpp:23]: (style) Variable 'aaa' is assigned a value that is never used.
[gcc_warning.cpp:59]: (style) Variable 'dir' is assigned a value that is never used.
[gcc_warning.cpp:65]: (style) Variable 'ret' is assigned a value that is never used.
[gcc_warning.cpp:28]: (error) Array 'foo[2]' accessed at index 2, which is out of bounds.
[gcc_warning.cpp:29]: (error) Array 'foo[2]' accessed at index 3, which is out of bounds.
[gcc_warning.cpp:30]: (error) Array 'foo[2]' accessed at index 4, which is out of bounds.
[gcc_warning.cpp:31]: (error) Array 'foo[2]' accessed at index 5, which is out of bounds.
[gcc_warning.cpp:53]: (error) Memory leak: p
[gcc_warning.cpp:53]: (error) Resource leak: fp
[gcc_warning.cpp:61]: (error) Resource leak: dir
Checking usage of global functions..
(information) Cppcheck cannot find all the include files (use --check-config for details)

从上述信息中可以清晰地看到哪些代码存在问题,原因也一一给出。根据提示去修改代码即可。

注1:只要是人写的代码,都有可能存在这种那种问题。代码问题关键因素还是人,但可以借助工具帮助我们减少错误。——至少,如果在某个角落中数组越界了,cppcheck能检测出来。
注2:上述代码无法通过g++编译,因为有很多错误。但cppcheck不是编译器,所以无法检测出来。
注3:cppcheck不是万能的,比如在一个函数申请二级内存,在另一个函数不释放,此情况有内存泄漏,但cppcheck检测不出来。
另外也可以参考笔者的文章:
gcc较高版本的一些编译警告收集
继续收集gcc一些编译警告
GCC编译警告选项的学习

李迟 2016.6.15 周三 晚