C之(15)cppcheck使用介绍
Author: Once Day Date: 2025年3月23日
一位热衷于Linux学习和开发的菜鸟,试图谱写一场冒险之旅,也许终点只是一场白日梦…
漫漫长路,有人对你微笑过嘛…
全系列文章可查看专栏: Linux实践记录_Once_day的博客-CSDN博客
参考文章:
- CppCheck静态代码检查工具教程【Windows和Linux端】-CSDN博客
- cppcheck的安装及基本使用_cppcheck这个操作只对当前安装的产品有效-CSDN博客
- Cppcheck - A tool for static C/C++ code analysis
文章目录
- C之(15)cppcheck使用介绍
- 1. cppcheck介绍
- 1.1 概述
- 1.2 检查结果
- 1.3 检查规则和过滤
- 2. 基本使用方法
- 3. 参数介绍
- 4. gitlab-ci集成
1. cppcheck介绍
1.1 概述
Cppcheck是一款静态代码分析工具,专门用于检查C/C++代码中潜在的错误和漏洞。它由瑞典程序员Daniel Marjamäki在2007年开发,旨在帮助开发者提高代码质量,减少bug。
Cppcheck的诞生源于Daniel在工作中遇到的一些棘手的编程问题。他发现,许多C/C++项目中存在许多常见的编码错误,如内存泄漏、未初始化变量、数组越界等,这些问题不易被发现,但却可能导致程序崩溃或产生安全隐患。为了解决这一痛点,Daniel利用业余时间开发了Cppcheck。
最初,Cppcheck只是一个简单的命令行工具,功能较为单一。但随着时间推移,在全球开源社区的共同努力下,Cppcheck不断完善,功能日益强大。目前,它已经成为了C/C++开发者必不可少的代码检查工具之一,被广泛应用于各种项目中,从小型单机程序到大型服务器软件,涵盖领域十分广泛。
Cppcheck的核心价值在于提升代码质量,减少潜在错误。它通过静态分析代码,能够精确地定位出各种问题,如unused functions、unexpectedsign、uninitialized variables等,并给出改进建议。同时,Cppcheck也提供了丰富的自定义规则,用户可以根据自己的需求进行个性化设置。
使用Cppcheck非常简单,通常只需一行命令即可。例如,要检查当前目录下的所有.cpp文件,只需执行:
cppcheck --enable=all .
Cppcheck就会自动分析每个文件,输出检查结果,并指出问题所在的行号和具体原因。用户可以根据这些信息快速定位并修复代码缺陷,大大提高开发效率。
除了命令行,Cppcheck还支持多种集成方式,可以与Visual Studio、Eclipse等流行IDE无缝对接,实现图形化操作。同时,Cppcheck也提供了Python绑定,方便进行二次开发和功能扩展。
1.2 检查结果
Cppcheck的检查结果主要分为以下几类:
(1)Error(错误):这是最严重的问题,意味着代码中存在明显的缺陷或漏洞,可能导致程序崩溃、数据损坏等危险后果。
- 内存泄漏:动态分配的内存没有被正确释放,长期积累可能耗尽系统资源。
- 悬空指针:指针指向了已经释放的内存区域,再次使用可能引发无法预料的错误。
- 除零错误:将一个数除以0会导致程序异常退出。
- 数组越界:访问了数组边界之外的内存,可能覆盖其他关键数据。
(2)Warning(警告):Warning通常指出了代码中的一些不规范或可疑的写法,虽然不会直接导致错误,但可能隐藏着潜在的bug。开发者应该仔细检查这些警告,采取防御性编程策略,以提高代码的健壮性。
- 未初始化变量:变量在使用前没有赋予初值,其内容是不可预知的。
- 死代码:一些永远不会被执行到的代码片段,通常暗示存在BUG。
- 忽略返回值:忽略了某些函数的返回值,可能错过了重要的错误信息。
(3)Style(编码风格):Style指出了代码的一些格式问题,虽然不影响程序的功能,但影响可读性、可维护性。
- 未使用的函数:定义了一些函数,但在代码中并未被调用,建议删除。
- 多余的代码:一些无用的变量定义、赋值、条件判断等,应该剔除。
- 缩进不规范:没有遵循统一的缩进标准,使代码显得混乱。
(4)Portability(移植性):Portability提示了代码在跨平台、跨编译器时可能遇到的兼容性问题。
- 大小端问题:不同CPU架构下,多字节数据的存储顺序可能相反。
- 数据类型长度:不同平台下,int、long等类型的字节长度可能不一致。
- 特定编译器的语法:使用了某些编译器特有的语法,在其他编译器下可能无法通过编译。
(5)Performance(性能):Performance给出了一些优化代码性能的建议,帮助开发者写出更高效的程序。
- 循环展开:建议将一些小循环展开,以减少迭代次数。
- 避免不必要的临时对象:在循环内部创建大量临时对象,建议放在循环外处理。
(6)Information(信息):Information是一些有趣的提示信息,不太重要,可以忽略。
- 幻数:代码中用到了一些未命名的神秘数字,难以猜测其含义。
- 文档格式:doxygen等文档注释的格式不标准。
1.3 检查规则和过滤
Cppcheck提供了丰富的检查规则,可以从多个维度审视代码质量。
检查规则 | 描述 |
---|---|
自动变量检查 | Cppcheck会检查自动变量的使用是否规范,如是否在使用前初始化、是否存在死代码等。这可以避免很多潜在的bug。 |
数组边界检查 | 数组越界是C/C++中常见的错误之一。Cppcheck可以准确定位出数组访问是否越界,提醒开发者注意边界条件的处理。 |
类检查 | Cppcheck对类的使用提供了全面的检查,如构造函数、析构函数的正确性,虚函数的覆盖情况,operator=的实现等,帮助写出更健壮的类。 |
过期废弃函数检查 | 某些老旧的C/C++函数已经被标记为过期或废弃,不建议继续使用。Cppcheck可以自动识别这些函数,建议开发者使用新的替代品。 |
异常的内存使用和释放 | Cppcheck会严格检查内存分配和释放的匹配情况,及时发现可能导致内存泄漏或非法访问的异常操作。 |
内存泄漏检测 | 借助Cppcheck,可以方便地定位内存泄漏发生的位置。Cppcheck主要通过分析指针的引用情况,判断动态分配的内存是否被正确释放。 |
系统资源管理 | 对于文件句柄、互斥锁等系统资源,Cppcheck也提供了有效的检测,确保它们被正确创建、使用和释放,不会长期占用。 |
STL使用检查 | Cppcheck内置了常见STL容器和算法的使用规则,可以发现一些错误的使用方式,如迭代器失效、算法前置条件不满足等。 |
代码格式与性能 | 除了纠正错误,Cppcheck还可以检查代码的格式规范性,提示一些可能影响性能的写法,如复杂度过高的函数、不必要的拷贝等。 |
尽管Cppcheck可以发现很多问题,但有时候我们可能希望忽略某些特定类型的警告,这时就可以使用--suppress
选项进行过滤。
例如,使用下面的命令可以屏蔽所有的shadowVariable
警告:
cppcheck --suppress=shadowVariable test.cpp
如果要屏蔽多个警告,可以用逗号分隔:
cppcheck --suppress=shadowVariable,unusedFunction test.cpp
除了按警告类型过滤,Cppcheck还支持更精细的过滤条件,如按文件、行号等。
2. 基本使用方法
在Ubuntu系统上安装和使用Cppcheck非常方便,下面我们就一步步来看看具体的操作过程。
打开终端,执行以下命令安装Cppcheck:
sudo apt-get update
sudo apt-get install cppcheck
等待安装完成后,可以通过–version选项检查安装是否成功:
cppcheck --version
如果显示了Cppcheck的版本号,说明安装成功。
要检查一个C/C++源文件,只需在终端中执行:
cppcheck test.cpp
Cppcheck就会自动分析test.cpp中的代码,并输出检查结果。
如果要检查多个文件,可以直接将它们作为参数传给Cppcheck:
cppcheck test1.cpp test2.cpp test3.cpp
要检查一个目录下的所有源文件,可以使用通配符:
cppcheck src/*.cpp
上述命令会检查src目录下的所有.cpp文件。
默认情况下,Cppcheck只会执行一些基本的检查。如果要启用所有检查,可以加上--enable=all
选项:
cppcheck --enable=all test.cpp
这会启用所有的警告和建议,可以更全面地发现潜在问题。
下面我们用一个具体的例子来演示Cppcheck的使用。假设我们有以下一个简单的C++程序(test.cpp):
#include <iostream>
using namespace std;int main() {int* p = new int[10];p[10] = 1; // 数组越界delete[] p;int a;cout << a << endl; // 使用未初始化的变量return 0;
}
这个程序有两个明显的错误:数组越界和使用未初始化的变量。下面我们用Cppcheck来检查它:
cppcheck --enable=all test.cpp
Cppcheck会输出类似以下的结果:
Checking test.cpp ...
test.cpp:1:0: information: Include file: <iostream> not found. Please note: Cppcheck does not need standard library headers to get proper results. [missingIncludeSystem]
#include <iostream>
^
test.cpp:6:6: error: Array 'p[10]' accessed at index 10, which is out of bounds. [arrayIndexOutOfBounds]p[10] = 1; // 数组越界^
test.cpp:10:13: error: Uninitialized variable: a [uninitvar]cout << a << endl; // 使用未初始化的变量^
test.cpp:9:9: style: Variable 'a' is not assigned a value. [unassignedVariable]int a;^
nofile:0:0: information: Active checkers: 161/592 (use --checkers-report=<filename> to see details) [checkersReport]
可以看到,Cppcheck准确地发现了这两个错误,并指出了对应的代码行。我们可以据此快速定位和修复这些问题。
3. 参数介绍
Cppcheck的基本语法格式为:
cppcheck [OPTIONS] [files or paths]
如果输入参数是目录,那么目录下面的*.cpp
, *.cxx
, *.cc
, *.c++
, *.c
, *.ipp
,*.ixx
, *.tpp
, *.txx
等文件会被依次递归的检查。
参数 | 描述 |
---|---|
–addon= | 执行插件,如 misra 等。如需额外选项需提供json配置文件 |
–addon-python= | 指定Python解释器路径,用于插件,优先级高于json配置 |
--cppcheck-build-dir=<dir> | 设置 Cppcheck 的工作目录,利于增量分析和全局分析 |
–check-config | 只检查 Cppcheck 配置,不作代码分析 |
--check-level=<level> | 配置检查深度,可选 normal (默认)、exhaustive |
–check-library | 显示库文件信息不完整时的提示消息 |
--checkers-report=<file> | 将所有已启用的检查器信息写入指定文件 |
--clang=<path> | 试验性质地使用 Clang 分析器作为代码解析前端 |
--config-exclude=<dir> | 设置预处理配置检查的排除路径前缀 |
--config-excludes-file=<file> | 从文件中读取配置检查的排除路径前缀 |
--disable=<id> | 关闭指定的检查类型,如missingInclude |
–dump | 以 xml 格式输出每个翻译单元的 AST、符号表等调试信息 |
-D | 定义预处理宏,可指定编译配置 |
-E | 输出预处理结果到标准输出,不做代码分析 |
–enable= | 启用额外检查,如 all、warning、style、performance、portability、information 等 |
–error-exitcode= | 发现错误时返回值为 n 而不是默认的0 |
–errorlist | 将所有错误消息以XML格式打印出来。 |
–exitcode-suppressions | 显示错误消息,但是不会造成退出返回值(–error-exitcode)为非0 |
--file-filter=<str> | 只分析文件名匹配 <str> 的文件,可多次使用 |
--file-list=<file> | 从文件中读取待分析的源文件列表 |
-f, --force | 强制检查所有编译配置,最后一次使用的 --max-configs 有效 |
–fsigned-char | 将 char 视为 signed char |
–funsigned-char | 将 char 视为 unsigned char |
–help, -h | 显示帮助信息 |
-I | 头文件搜索路径,相对路径无需设置 |
--include=<file> | 在被检查文件之前强制包含某文件 |
--includes-file=<file> | 从文件中读取包含路径 |
-i | 排除某个源文件或目录不做检查 |
–inconclusive | 允许 Cppcheck 输出不确定的分析结果 |
–inline-suppr | 允许使用行内抑制注释 |
-j | 并行检查的线程数 |
-l <load> | 设置负载阈值,控制启动新线程的时机 |
--language=<language> | 强制将所有文件作为指定语言(c、c++)来分析 |
–library= | 加载包含类型、函数信息的库文件。用于理解代码 |
–max-configs= | 单文件最大检查的编译配置数,默认12,和 -f 参数配合使用 |
–max-ctu-depth=N | 全局分析的最大迭代深度,默认为2 |
--output-file=<file> | 将结果输出到文件而不是标准错误流 |
–platform= | 指定特定平台的数据类型大小,可选如 unix32、win64 等 |
--plist-output=<path> | 在指定目录下生成 Clang-plist 格式的输出文件 |
–project= | 分析工程文件,支持.sln、.vcxproj、compile_commands.json 等 |
--project-configuration=<config> | 在分析 .sln 或 .vcxproj 时指定欲分析的项目配置 |
-q, --quiet | 不显示进度,但不影响详细信息的显示 |
-rp, --relative-paths= | 输出结果使用相对路径 |
–report-progress | 在分析每个文件时报告进度(仅单线程) |
–rule= | 匹配正则表达式规则的警告信息 |
--rule-file=<file> | 使用指定的规则文件 |
--showtime=<mode> | 输出不同粒度的性能分析数据 |
–std= | 设置标准,如 c99、c++11 |
–suppress= | 抑制匹配 格式的警告 |
--suppressions-list=<file> | 从文件读取警告抑制列表 |
--suppress-xml=<file> | 从xml文件读取警告抑制列表 |
–template=‘’ | 定制输出格式 |
--template-location='<text>' | 定制错误信息的位置输出格式 |
-U | 取消预定义的宏,用于隐藏特定的 #ifdef 代码分支 |
-v, --verbose | 输出更详细的错误信息 |
-version | 显示 Cppcheck 版本号 |
–xml | 将结果以 XML 格式输出到错误流(stderr) |
Cppcheck 还有一些其他参数未在表格中列出,具体可参考完整的命令行帮助信息。
4. gitlab-ci集成
下面展示一种将Cppcheck集成到gitlab ci流水线的方法,GitHub和Gitee都可以以此作为参考:
首先需要一个运行Cppcheck的脚本run_cppcheck.sh
,在该脚本里指定Cppcheck运行参数和处理结果:
# 当前目录路径
export SOURCE_DIR=${SOURCE_DIR:-$(pwd)}# 定义参数
# 除了 missingInclude 其他都是默认开启的
CPP_ARGS="--enable=style,performance,portability,unusedFunction,information"
CPP_ARGS="$CPP_ARGS --disable=missingInclude"
# 忽略不确定的检查
CPP_ARGS="$CPP_ARGS --inconclusive"
# 强制检查所有文件
CPP_ARGS="$CPP_ARGS --force"
# 忽略内联的警告
CPP_ARGS="$CPP_ARGS --inline-suppr"
CPP_ARGS="$CPP_ARGS --library=posix"
# 指定C和C++的标准
CPP_ARGS="$CPP_ARGS --std=c++11 --std=c11 --fsigned-char"
# 发现错误时 exit-code = 1
CPP_ARGS="$CPP_ARGS --error-exitcode=1"# set -x
# 对代码进行静态检查
cppcheck $CPP_ARGS $SOURCE_DIR/anmk/
if [ $? -ne 0 ]; thenecho "Cppcheck failed"exit 1
fiecho "Cppcheck success"
exit 0
首先,脚本设置了 SOURCE_DIR 环境变量,表示源代码的根目录路径,默认为当前目录。
接下来,通过 CPP_ARGS 变量定义了传递给 Cppcheck 的各种参数:
--enable
: 启用额外的检查,如编码风格、性能、可移植性、未使用函数、信息提示等。--disable
: 禁用 missingInclude 检查。--inconclusive
: 允许 Cppcheck 报告不确定的结果。--force
: 强制检查所有文件和编译配置。--inline-suppr
: 支持使用行内抑制注释。--library
: 使用 POSIX 库进行分析。--std
: 指定 C 和 C++ 的语言标准。--error-exitcode
: 发现错误时返回非零的退出码。
然后,脚本运行 Cppcheck 命令,将 SOURCE_DIR/anmk/ 目录下的代码作为分析对象,应用之前定义的所有参数。
通过检查 Cppcheck 的返回值,如果不为0,说明存在静态分析问题,脚本输出错误信息并以非零状态码退出;反之则代表检查通过,脚本输出成功信息并正常退出。
.gitlab-ci.yml
配置如下:
# 本地构建阶段 - 代码静态检查, 存在静态检查问题则失败
develop-static-check:stage: local-buildtags:- anmk-buildneeds: []script:- echo "Check ANMK Project - Develop Version - Static"- ./run_cppcheck.sh- echo "Check finished"
定义了一个名为 develop-static-check
的job,用于对开发版本的代码进行静态检查。
该job在 local-build 阶段运行,使用 anmk-build 的标签来选择合适的runner。
needs: []
表示该job不依赖于其他任务,可以独立运行。
script 部分定义了该job的运行脚本:
- 输出提示信息,表示开始对开发版本的ANMK项目代码进行静态检查。
- 调用 run_cppcheck.sh 脚本执行实际的静态分析过程。
- 输出提示信息,表示静态检查已完成。
通过这样的集成,每次提交或合并请求时,GitLab 都会自动触发 develop-static-check job,对代码进行 Cppcheck 静态分析。如果发现问题,流水线会失败并及时反馈,促使开发者尽快修复。
这种自动化的静态代码检查可以帮助提早发现代码缺陷,提高代码质量,减少后续的调试和维护成本。
Once Day
也信美人终作土,不堪幽梦太匆匆......
如果这篇文章为您带来了帮助或启发,不妨点个赞👍和关注,再加上一个小小的收藏⭐!
(。◕‿◕。)感谢您的阅读与支持~~~