【经验分享】关于静态分析工具排查 Bug 的方法

文章目录

  • 编译器的静态分析
  • cppcheck
    • 安装 cppcheck
    • 运行 cppcheck

程序员的日常工作,不是摸鱼扯皮,就是在写 Bug。虽然这是一个梗,但也可以看出,程序员的日常一定绕不开 Bug。而花更少的时间修复软件中的 Bug,且不引入新的 Bug,最好的办法就是使用静态分析工具排查 Bug。

所谓的静态分析,顾名思义,是指在不运行程序的情况下对源代码进行检查,通过算法和模式识别来检测可能存在的错误、漏洞或不符合编码规范的地方。这种方法不仅可以帮助开发者在早期阶段发现 Bug,还能提高代码质量和安全性,减少后期调试的时间和成本。

但是在讨论静态分析工具之前,你知道编译器也进行静态代码分析吗?

编译器的静态分析

编译器的作用,就是生成可执行文件或二进制文件,因此,在默认情况下,编译器并不用于静态代码分析。但随着技术的发展和版本的迭代更新,编译在静态代码分析方面不断改进并变得更好,不同版本的编译器在编译同一个代码时,出现警告数量可能都不一样。

对很多初学者来说,编译器生成的警告非常烦人,并且大多数消息并不关键或不会对应用程序造成任何问题。其实我们应该信任编译器,因为有一件事是肯定的:没有人比编译器本身更了解语言、语法和语义。

那么编译器要怎么进行静态分析呢?

以下面的代码为例,我用不同的编译器进行编译并运行,

#include <stdio.h>#define ON  0xFF
#define OFF 0x00void print_message(char status)
{if (status == ON)printf("ON\n");elseprintf("OFF\n");
}int main()
{print_message(ON);return 0;
}

这段代码看起来没有任何问题,而且看起来运行后会打印 ON,而实际上却打印 OFF

以 GCC 编译编译运行,结果如下:

在这里插入图片描述

如果源代码是用 Clang 编译器编译,会立刻给我们一个警告:

在这里插入图片描述

[!WARNING]

这是警告的具体意思,是一个整型常数 255 和一个 char 类型的表达式真正进行比较,代码可能存在逻辑上的问题。

可见 Clang 不仅是一个编译器,也是一个非常不错的静态分析工具。当然,这类警告 GCC 也可以发现,不过需要在编译时,加上两个参数 -Wall-Wpedantic。如下图:

在这里插入图片描述

[!NOTE]

-Wall-Wpedantic 的作用是什么?

  1. -Wall 参数告诉 GCC 启用所有非默认的警告选项。这意味着 GCC 将会报告许多常见的编程错误,包括但不限于未使用的变量、可疑的类型转换、可能的除零错误、条件表达式中的常量等。-Wall 可以帮助开发者找到那些可能在编译时没有错误但在运行时会导致问题的代码缺陷。

    例如,以下是一些由 -Wall 开启的警告:

    • uninitialized: 使用未初始化的变量;
    • unused: 定义了但未使用的变量或函数;
    • conversion: 类型转换可能导致数据丢失;
    • pointer-sign: 指针比较中正负符号的差异;
    • format-security: 格式字符串的安全问题;
    • overflow: 数值运算溢出的风险。
  2. -Wpedantic 参数使 GCC 更加严格地遵守标准,它将警告所有不符合 C 或 C++ 标准的代码。这意味着任何与标准不完全一致的语法或行为都会被标记出来,即使这种行为在 GCC 中可能是默认允许的。

    -Wpedantic 主要关注的是标准一致性问题,比如:

    • 使用扩展的语法,如 GNU 特有的语法;

    • 不符合标准的声明或初始化;

    • 未在标准中明确规定的编译器行为;

    • 对于 C++,它还会警告关于 C++11 或更高版本标准中不再推荐的用法;

值得注意的是, -Wpedantic 可能会警告以下情况:

  • 使用 C99 的特性(如复合字面量)在 C89 模式下编译。
  • 在 C++ 中使用 C 风格的字符串或旧的初始化语法。
  • 使用了在标准中没有定义的行为,比如某些类型的隐式转换。

通常,为了最大程度地提高代码质量,开发者会同时使用 -Wall-Wpedantic。这有助于确保代码不仅避免了常见的编程错误,而且也遵循了语言标准,从而提高了代码的可读性和可移植性。

然而,值得注意的是,-Wpedantic 可能会产生大量警告,特别是在处理旧代码或使用了某些非标准但实用的 GNU 扩展时。因此,在实际应用中,开发者可能需要根据具体情况调整警告级别,以避免被过多的警告信息淹没。

由此可见,作为开发者,我们应该注意所有编译器警告。甚至我们需要能够生成更多警告的工具,那种可以分析源代码并发现语言的奇怪结构、潜在问题和不寻常用法的工具。

Clang 和 GCC 在静态分析方面还是比较出色的,但这不是它们的主要作用,这些警告是在编译过程中完成的简单检查的副产品而言。此外,静态代码分析需要大量时间,并且通常不需要每次编译。编译时间对于编译器来说非常重要。在检查代码质量和编译时间之间需要权衡。

正是如此,也促使了静态代码分析工具的产生,这类工具也称为 lint,其中一个开源的静态分析工具 cppcheck,也是本文的主角。

cppcheck

Cppcheck 是 C/C++ 代码的静态分析工具,它提供独特的代码分析来检测错误,并专注于检测未定义的行为危险的编码结构,其中包括以下几个方面的内容:

  • 野指针或悬空指针
  • 除以零
  • 整数溢出
  • 无效的位移操作数
  • 无效转换
  • STL 的无效使用
  • 内存管理
  • 空指针取消引用
  • 越界检查
  • 未初始化的变量
  • 写入常量数据
  • 未使用或重复的代码

Cppcheck 是一个开源项目,目前托管在 Sourceforge 和 GitHub 上,支持 GNU/Linux、Windows 和 Mac OS 操作系统。

安装 cppcheck

安装 cppcheck 的步骤可在 Cppcheck 官网可以找到。

也可以从 Linux 发行版包管理器获取 cppcheck,以 Ubuntu 系统为例,可以运行以下命令来安装 cppcheck:

sudo apt update
sudo apt install cppcheck

如果要使用最新的 cppcheck 版本,则需要下载源代码,编译并安装,过程如下:

wget https://github.com/danmar/cppcheck/archive/1.90.tar.gz
tar xfv 1.90.tar.gz
cd cppcheck-1.90/
make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck HAVE_RULES=yes -j4
sudo make MATCHCOMPILER=yes FILESDIR=/usr/share/cppcheck HAVE_RULES=yes install

运行 cppcheck

以下面这段代码为例,代码作用为计算整数数组的所有元素的总和:

#include <stdio.h>int buf[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};int calc(void)
{int result;int i;for (i = 0; i <= 10; i++)result += buf[i];return result;
}int main()
{calc();return 0;
}

这段代码其实有两处风险,但是 GCC 编译器(版本号为 11.4.0)无法找到这些风险:

在这里插入图片描述

即使加上了 -Wextra-Werror 两个参数,也找不到任何风险:

在这里插入图片描述

[!NOTE]

-Wextra-Werror 的作用是什么?

  1. -Wextra 标志开启了一系列额外的警告,这些警告超出了默认警告的范畴。该参数的目的是捕捉那些可能不易察觉的编程错误,帮助开发者避免未来可能遇到的运行时错误和维护问题。它包含了多个单独的警告标志,涵盖了可能被忽略或不常见的代码问题,包括但不限于以下警告:

    • 使用未初始化的变量;
    • 形参与实参的类型不匹配;
    • 赋值运算符可能被误解为等于运算符;
    • 可能的枚举值溢出;
    • 函数调用的返回值被忽视;
    • 以及其他潜在的代码错误和不良实践。
  2. -Werror 标志的作用是将所有警告视为错误。换句话说,当编译器遇到任何被 -W 标志开启的警告时,它将停止编译过程,就像遇到了一个真正的编译错误一样。这迫使开发者在编译通过之前必须解决所有的警告。

    -Werror 的主要优点是强制执行代码质量标准,确保没有潜在的编程问题遗留到最终的二进制文件中。这在团队开发环境中特别有用,可以确保所有代码都达到一致的高标准。

现在换 Clang 编译器进行编译,同样也无法找到这些风险:

在这里插入图片描述

Clang 编译器也可以使用与 GCC 相同的参数,如下命令:

clang -Wall -Wextra -Werror -Wpedantic main.c -o main

执行命令后,可以找到两个错误:

在这里插入图片描述

但是这两个错误其实是重复的,引起两个错误的原因都是变量 result 未初始化,如果给 result 初始化后,就不会出现这个错误,如下图:

在这里插入图片描述

把程序改回原来的代码,用 cppcheck 检查,结果可以找到这两个错误,如下图:

在这里插入图片描述

除了 Clang 找出来的 result 未初始化之外,cppcheck 还在 for 循环中找到了数组越界。

[!CAUTION]

calc 函数的 for 循环中,使用了 i <= 10 作为循环终止条件,然而数组 buf 的下标是从 0 到 9(共10个元素)。当程序尝试访问 buf[10] 时,这实际上是越界访问,因为 buf[10] 并不存在(数组最后一个元素是 buf[9])。

由此可见,编译器并不能完全充当静态分析工具,逻辑上的错误还是要靠专业的静态分析工具。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/47305.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#自定义异常(Exception)的实现

1、自定义异常类 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace ExceptionApp {public class CustomException:Exception{//默认构造函数public CustomException():base() { }//接收错误信…

leetcode hot 100 刷题记录(medium)

题目3&#xff1a;无重复字符的最长子串&#xff08;YES&#xff09; 解题思路&#xff1a;其实最好想到的方法就是使用两层for,让每个字符都可以是子串的首字符&#xff0c;查看哪个子串的长度最长即可。 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长 子…

lightgbm

lightGBM 1.sklearn 使用代码 【机器学习基础】XGBoost、LightGBM与CatBoost算法对比与调参 首先&#xff0c;XGBoost、LightGBM和CatBoost都是目前经典的SOTA&#xff08;state of the art&#xff09;Boosting算法&#xff0c;都可以归类到梯度提升决策树算法系列。三个模…

探索LangChain的单元测试世界:主流框架全解析

探索LangChain的单元测试世界&#xff1a;主流框架全解析 引言 在软件开发过程中&#xff0c;单元测试是确保代码质量的关键环节。LangChain作为一个多语言编程工具链&#xff0c;支持多种编程语言&#xff0c;每种语言都有其对应的单元测试框架。本文将详细介绍LangChain支持…

5. JavaSE ——【适合小白的数组练习题】

&#x1f4d6;开场白 亲爱的读者&#xff0c;大家好&#xff01;我是一名正在学习编程的高校生。在这个博客里&#xff0c;我将和大家一起探讨编程技巧、分享实用工具&#xff0c;并交流学习心得。希望通过我的博客&#xff0c;你能学到有用的知识&#xff0c;提高自己的技能&a…

【区块链 + 智慧政务】澳门:智慧城市建设之证书电子化项目 | FISCO BCOS应用案例

2019 年 2 月 27 日&#xff0c;澳门政府设立的澳门科学技术发展基金与微众银行达成合作&#xff0c;通过区块链、人工智能、大数据、 云计算等创新技术&#xff0c;共同推进澳门特区的智慧城市建设与未来型城市发展&#xff0c;提升粤港澳大湾区的科创能力。在澳 门智慧城市建…

【数学建模】高温作业专用服装设计(2018A)隐式差分推导

为方便计算&#xff0c;对区域进行离散化处理&#xff0c;采用隐式差分格式进行离散计算。隐式差分格式如图&#xff1a; 每层材料内部 对第 j j j层材料: 其中&#xff0c; λ j \lambda_j λj​表示第 j j j层的热扩散率&#xff0c; c j c_j cj​表示第 j j j层的比热容…

Matplotlib库学习之pyplot.figure()函数

Matplotlib库学习之pyplot.figure()函数 一、简介 pyplot.figure() 是 Matplotlib 的 pyplot 模块中的一个函数&#xff0c;用于创建一个新的图形&#xff08;figure&#xff09;。在 Matplotlib 中&#xff0c;图形是绘图元素的容器&#xff0c;可以包含多个坐标轴&#xff…

linux需要熟悉的命令理解记忆

(1)光标插入 (1)一般模式下: i 插入到光标前方 记忆方法:在一般模式下, 光标选中字符, 我们按下 i, 就会插入光标的前方, insert, 表示插队 (2)一般模式下: a 插入到光标后方 记忆方法: 在一般模式下, 光标选中字符,a表示append, 添加或者附加的意思 (3) 如果要在行首或者行…

css实现每个小盒子占32%,超出就换行

代码 <div class"visitors"><visitor class"item" v-for"(user,index) in userArr" :key"user.id" :user"user" :index"index"></visitor></div><style lang"scss" scoped&…

java乱码问题

文章目录 1.eclipse所有修改编码的地方2.io读取文件乱码问题1.读写统一2.转换字符编码&#xff1a; 3.http请求返回乱码 1.eclipse所有修改编码的地方 2.io读取文件乱码问题 1.读写统一 如果文件是以UTF-8编码保存的&#xff0c;那么在读取文件时也应使用UTF-8编码。 2.转换…

Apple Vision Pro 和其商业未来

机器人、人工智能相关领域 news/events &#xff08;专栏目录&#xff09; 本文目录 一、Vision Pro 生态系统二、Apple Vision Pro 的营销用例 随着苹果公司备受期待的进军可穿戴计算领域&#xff0c;新款 Apple Vision Pro 承载着巨大的期望。 苹果公司推出的 Vision Pro 售…

百分点科技签约潍坊市数据产业发展战略合作

近日&#xff0c;潍坊市数据产业发展战略合作签约仪式举行&#xff0c;潍坊市人民政府副市长张震生&#xff0c;潍坊市财政局党组书记、局长王金祥&#xff0c;潍坊市大数据局党组书记陈强出席大会并致辞。百分点科技受邀进行战略合作签约&#xff0c;共同见证潍坊市数据要素市…

生成式人工智能(AI)的未来

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

深度学习模型快速开发平台推荐

前言 本文面向深度学习初学者或者工程师&#xff0c;推荐几个常用的深度学习模型快速开发平台。可以帮助初学者快速跑通模型&#xff0c;帮助工程师快速对模型进行部署和应用。 huggingface 简介 不多介绍&#xff0c;全球最大的模型托管平台&#xff0c;该平台最大的特点是…

全网超详细Redis主从部署(附出现bug原因)

主从部署 整体架构图 需要再建两个CentOs7,过程重复单机部署 http://t.csdnimg.cn/zkpBE http://t.csdnimg.cn/lUU5gLinux环境下配置redis 查看自己ip地址命令 ifconfig 192.168.187.137 进入redis所在目录 cd /opt/software/redis cd redis-stable 进入配置文件 vim redi…

C++——类和对象(初始化列表和运算符重载与静态与友元)

文章目录 初始化列表语法结构定义和使用案例初始化列表初始化常量成员&#xff1a; 注意事项 运算符重载加法运算符重载语法结构示例注意事项 减法运算符重载语法结构示例注意事项 等于运算符重载等于运算符 的重载语法示例注意事项 大于运算符重载大于运算符 > 的重载语法…

mac如何查看cpu和显卡温度

在Mac上查看CPU和显卡温度&#xff0c;你可以使用以下几种方法&#xff1a; 方法1&#xff1a;使用内建工具“活动监视器” 虽然“活动监视器”不能直接显示温度信息&#xff0c;但它可以显示CPU使用情况等信息。 打开“活动监视器”&#xff0c;可以通过以下路径找到&#…

JavaScript 常见数学用法

JavaScript 数学 JavaScript Math 对象允许您对数字执行数学任务。 实例&#xff1a; Math.PI; 返回 3.141592653589793常见案例 Math.round() Math.round(x) 的返回值是 x 四舍五入为最接近的整数&#xff1a; 实例&#xff1a; Math.round(6.8); // 返回 7 Math.r…

JavaWeb入门程序解析(Spring官方骨架、配置起步依赖、SpringBoot父工程、内嵌Tomcat)

3.3 入门程序解析 关于web开发的基础知识&#xff0c;我们可以告一段落了。下面呢&#xff0c;我们在基于今天的核心技术点SpringBoot快速入门案例进行分析。 3.3.1 Spring官方骨架 之前我们创建的SpringBoot入门案例&#xff0c;是基于Spring官方提供的骨架实现的。 Sprin…