C++小知识:用合适的工具来分析你的代码

静态代码分析工具可简化编码过程,检测出错误并帮助修复。有个国外团队检测了 200 多个 C/C++ 开源项目,包括了 Php、Qt 和 Linux 内核等知名项目。于是他们每天分享一个错误案例,并给出相应建议。今天的案例来自 LibreOffice 项目。

错误代码

BOOL WINAPI DllMain( HINSTANCE hinstDLL,
  DWORD fdwReason, LPVOID lpvReserved )
{
 ....
 CreateThread( NULL, 0, ParentMonitorThreadProc,
 (LPVOID)dwParentProcessId, 0, &dwThreadId );
 ....
}

解释:

很久以前,我曾经在业余时间接过一些项目。有一次我就接了一个项目,但是最后没有搞定。这个项目本身就有问题,但是当时我并不知道。更麻烦的是,这个项目乍一看还蛮简单的。

其实就是在 DllMain 方法中,当某些条件触发时,用 Windows API 函数实现一些功能。我记不太清楚要实现哪些功能了,但是肯定不难。

我花了大量时间做这个项目,但是代码就是不工作。更糟糕的是,如果我创建一个标准的新应用,这段代码就没问题,一旦我把代码放到 DllMain 里去运行就不行。简直是个谜,不是吗?我最后还是没有找出问题的根源。

多年以后的今天,我使用?PVS-Studio 开发工具后,我突然意识到当年问题的原因。你瞧,其实 DllMain 函数能正确执行的操作非常有限,因为(很多操作依赖的)DLL 库并没有被加载,所以你不能直接在 DllMain 里就直接执行任意的操作。

我们现在有了诊断工具,可以提醒程序员在 DllMain 里直接用哪些操作是危险的。现在我终于明白那时程序不能运行的原因了。

关于不能在 DllMain 里执行哪些操作的更多细节,可以查看(PVS-Studio)关于 V718 诊断信息的描述。

所以,上面那段 LibreOffice 的代码片段很可能就无法工作。它能不能正常执行完全要靠运气。

正确的代码:

要修复这类错误其实很难。你需要重构整个代码逻辑,让 DllMain 函数里的操作越简洁越好。

建议:

对于这类问题并没有什么特别的建议。你不可能什么都知道,每个人总有一天都会遇到类似的谜题。我认为一个比较普遍的建议是这样的:请仔细地阅读和工作相关的各种文档。但你还是要明白,人们无法预测每一个可能出现的问题。如果你把所有的时间都拿去阅读文档了,那又怎么有时间去编程呢?即使你已经读了很多页的文档,你也不确认有没有漏看了某个文档,而它是可以让你免于犯错的。

我希望能给出更加实用的建议(来避免这些难以捕捉的错误),但是很遗憾我只能想到一条:使用静态分析工具。当然这还是不能保证你就不会犯错了。但是至少这么做会让你犯错的几率降低。如果当年我有了这些工具,那我就绝对不会在 DllMain 里去调用我写的那些代码,那么我很可能就能节省大量时间,少死好多脑细胞。要知道,我对当时没能搞定那个任务一直耿耿于怀!

这个错误由 PVS-Studio 静态分析工具捕获。错误文本:V718:“CreateThread”方法不应该在“DllMain”方法中调用。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。如果你想了解更多相关内容请查看下面相关链接

(0)

相关推荐

  • 剑指offer之C++语言实现链表(两种删除节点方式)

    1 问题 用C++语言实现链表 2 代码实现 #include <iostream> #include <stdlib.h> using namespace std; class List { public: List(); ~List(); List* createNode(int value);//创建节点 bool insertNode(List *node);//插入节点 void printList();//打印节点 bool deleteNode(List *node)

  • C++实践分数类中运算符重载的方法参考

    [项目-分数类中的运算符重载] (1)实现分数类中的运算符重载,在分数类中可以完成分数的加减乘除(运算后再化简).比较(6种关系)的运算. class CFraction { private: int nume; // 分子 int deno; // 分母 public: //构造函数及运算符重载的函数声明 }; //重载函数的实现及用于测试的main()函数 (2)在(1)的基础上,实现分数类中的对象和整型数的四则运算.分数类中的对象可以和整型数进行四则运算,且运算符合交换律.例如:CFrac

  • C++实践数组类运算的实现参考

    [项目-数组类运算的实现] 设计数组类Array,为了实现测试函数中要求的功能,请补足相关的函数(构造.析构函数)和运算符重载的函数. 实现策略提示:可以将测试函数中的语句加上注释,取消一句的注释,增加相应的函数,以渐增地实现所有的功能,避免全盘考虑带来的困难. class Array { private: int* list; //用于存放动态分配的数组内存首地址 int size; //数组大小(元素个数) public: //成员函数声明 }; //要求测试函数能够运行出正确.合理的结果:

  • C++小知识:不要去做编译器的工作

    对于C++编程的老鸟来说,有时候他们喜欢把一些东西按照编译器的工作原理进行改写,以便提高代码的运行效率.这么做确实高明,也能体现出程序员的水平,但是这么做也是有风险的.因为有时候你可能会因为一些简单的笔误,而造成非常难以察觉的错误.本文就给出了类似的例子. 这个Bug 出现在MySQL源代码中. 错误代码: static int rr_cmp(uchar *a,uchar *b) { if (a[0] != b[0]) return (int) a[0] - (int) b[0]; if (a[

  • C++实践Time类中的运算符重载参考方法

    [项目-Time类中的运算符重载] 实现Time类中的运算符重载. class CTime { private: unsigned short int hour; // 时 unsigned short int minute; // 分 unsigned short int second; // 秒 public: CTime(int h=0,int m=0,int s=0); void setTime(int h,int m,int s); void display(); //二目的比较运算符

  • Python使用ctypes调用C/C++的方法

    python使用ctypes调用C/C++ 1. ctpes介绍 ctypes is a foreign function library for Python. It provides C compatible data types, and allows calling functions in DLLs or shared libraries. It can be used to wrap these libraries in pure Python. 官方文档地址: https://do

  • Android Java调用自己C++类库的实例讲解

    Android Java 如何调用自己的 C++ 的类库 下面以 Java 调用 C++ 的加法运算函数为例,做简单说明. (使用 Android Studio 3 编译) 首先编译 c++ 类库 创建独立目录存放 c++ 文件,例如 "app/src/main/cpp/add.cpp",内容如下 #include <jni.h> extern "C" JNIEXPORT jint JNICALL Java_com_example_liyi_demo_U

  • C++实践排序函数模板项目的参考方法

    [项目-排序函数模板] 已知 void Sort(int a[],int size); void Sort(double a[],int size); 是一个函数模板的两个实例,其功能是将数组a中的前size个元素按从小到大顺序排列.试设计这个函数模板. 参考解答: #include<iostream> using namespace std; template<class T> void Sort(T set[],int n) { int i,j; T temp; for(i=1

  • C++二维数组中数组元素存储地址的计算疑问讲解

    关于二维数组中数组元素的存储地址,有同学问出了个好问题. 在我的课件中,出现了下面的讲解: a[i][j]的地址是p+(i*col+j)*d(d是数组元素所占的字节数). 同学编程序进行验证,出问题了: 地球停止转动了也必须先赞这种学习方式! 同学问:"老师,第一张图的4,我怎么觉得不对呢?第二张图我没4,结果好像也对,这里面差在哪呢?" 我的答复:"两个都对." 第一张图在讲原理,是我们在人脑里面要以"字节"为单位计算,p为首地址,单位是字节,

  • C++小知识:C/C++中不要按值传递数组

    错误的代码: ID_INLINE mat3_t::mat3_t( float src[ 3 ][ 3 ] ) { memcpy( mat, src, sizeof( src ) ); } 说明: 有时候程序员会忘记 C/C++ 里数组不能按值传递给函数.当你试图这样做时,是数组的指针(第一个元素的地址)而不是整个数组被传递.我们还应该记住,方括号中的数字没有任何意义.它们仅仅是程序员所做的标志,记录了传递数组的『假定』大小.事实上,你也可以传递一个大小完全不同的数组.例如,下面的代码就会成功编译

随机推荐