C + + 编码中减少内存缺陷的方法和工具
目录
2.3使用 Purify 和 Insure + + 来查找运行时内存缺陷。
3利用 VC + + 环境的调试和诊断功能,检查和查找常见的内存缺陷。
4. 使用 Windows 结构化异常处理机制来处理已发布版本软件的内存崩溃
摘要:C + + 语言是桌面系统,特别是系统软件和大型应用软件的主流开发语言。C + + 语言以其灵活性而著称,但它也更加复杂。使用 c + + 编写健壮的代码更具挑战性。C + + 允许动态内存管理,但是它也会导致更多与内存相关的问题。一般来说,除了系统设计上的缺陷之外,基于 c + + 的软件的大多数缺陷和错误都与内存缺陷有关(主要包括内存访问错误和内存泄漏)。因此,消除代码中与内存相关的缺陷已成为程序员编写、调试和维护代码的任务,也是保证软件质量的关键。本文结合一个863项目的工程实践,总结了如何利用 c + + 语言机制、开发环境和相关的质量保证工具来预防和发现编译、运行时和内存相关缺陷的各种方法和工具。
1遵循 c + + 相关的编码标准和习惯用法以防止缺陷。
编码标准是与语言相关的规则,是通过实践总结出来的经验。好的编程标准将有效地帮助开发人员避免开发潜在的危险代码。一般来说,为了减少内存缺陷,应该遵循以下编码规则[1] :
(1)基类或具有虚函数的类应将其析构函数声明为虚函数。
(2)防止构造函数中的内存泄漏,不要在析构函数中引发异常。
(3)使用相应的 new 和 delete 格式。也就是说: 使用 delete 释放 new 请求的内存,delete []释放 new []请求的内存。
(4)指针在使用前必须初始化,释放后指向动态内存的指针应立即设置为空。
如果在类构造函数中分配了资源,那么您需要显式地提供复制建构子和赋值操作符,并释放析构函数中的资源。
值得注意的是 c + + 中的习惯用法 RAII。RAII 的核心思想是使用对象来管理资源,在对象的构造函数中获取资源,并在其析构函数中释放资源。为了确保动态请求的内存即使在发生异常时也能被释放,理想的方法是使用局部变量来管理动态内存的所有权,即所谓的智能指针。STL 中的 auto _ ptr 是为了解决资源所有权问题而设计的,但它缺乏对引用号和数组的支持,不能用于 STL 容器中。Boost 库[3]提供的智能指针相对成熟,具有很高的实用价值。其中,shared _ ptr 是线程安全的,可以在 STL 容器中使用。具体例子的参考文献[3]。
编码规范检查的工具有:CodeWizard,PC-lint等。
1.1 CodeWizard
CodeWizard 可以直接自动扫描、分析和检查源代码。一旦发现违规行为,将生成信息,告知和解释哪些规则不符合规定。以 CodeWizard 4.3为例,它包含500多个编码标准。CodeWizard 可以选择要为当前项目实现的编码标准。CodeWizard 可以与 VC + + 紧密集成。安装后,在 VC + + 中有一个 CodeWizard 工具栏。
1.2 代码检查工具 PC-Lint
PC-Lint 可以检查编译器不容易找到的错误。PC-Lint 可以检查超过100个 c 库函数,并且可以在标准 c/c + + 代码中发现超过1,000个常见错误。要将 PC-lint 与 visualstudio 集成,您需要自己配置它。Jon Zyzyck 提供了一个报告生成器,可以帮助完成这项任务。你可以在 google http://www.ddj.com 下载。文献[4]解释了如何在 VC + + 环境中集成 PC-Lint。
2. 使用语言机制、开发环境和相关工具来防止和发现内存缺陷
发现问题是解决问题的前提。与修复内存缺陷相比,发现内存缺陷并准确定位引起缺陷的代码更加费时费力。早期和准确地检测内存缺陷对于提高开发效率非常重要。
2.1使用断言尽早暴露内存缺陷
断言是布尔调试语句,用于检测程序运行时某个条件的值是否始终为真。断言通常用于确认函数的输入和输出,检查对象的当前状态是否合法等等。在以下场景中使用断言可以帮助查找与非法内存访问相关的错误:
(1)验证指针是否可读/可写。
在函数的入口处,通常需要验证指针指向的内容区域是否可读/可写。通常使用 assert 的形式(p!= NULL)检测。但是,指针的值不为空并不意味着指针指向合法的可读/可写内存。Win32 API 提供了函数 IsBadReadPtr、 IsBadWritePtr、 IsBadStringPtr、 IsBadCodePtr 来检测指针指向的内存区域是否可读/可写。C 运行时库提供了 _crtis ValidPointer、 _crtisvalidheapter 和其他函数,MFC 库提供了 AfxIsValidAddress 和 AfxIsValidString 函数来完成类似的函数。
(2)对于基于 mfc 的程序,ASSERT _ valid 宏调用重载的 AssertValid 函数,以确定指向 cobject 派生类对象的指针是否有效。ASSERT _ valid 宏主要调用 AfxIsValidAddress 函数和 cobject 派生类对象的 AssertValid 函数(参见 MFC 源代码 afx.h,objcore.cpp)。
2.2使用 c 运行时库检查内存泄漏
VC + + 的 c 运行时库(CRT)提供了广泛的函数来帮助用户检测内存泄漏。提供了 CrtMemCheckPoint、 CrtDump MemoryLeaks、 CrtSetDbgFlag 和其他函数来帮助调试内存泄漏。
对于非 mfc 项目,要启用有效的内存泄漏报告功能,需要进行设置。
2.3使用 Purify 和 Insure + + 来查找运行时内存缺陷。
Rational Purify 和 Parasoft Insure + + 是运行时错误检查的工具。Purify 主要检测: 读/写超出数组内存的边界,使用未初始化的内存,对释放的内存进行读/写,内存泄漏等。Insure + + 使用其专利技术(源代码插装和运行时指针跟踪)来查找大量的内存操作错误,报告错误的源代码行和执行跟踪。根据作者的测试(基于98个存在各种内存错误的 c + + 程序,覆盖典型情况) ,Insure + 6.1可以准确地检测出。
3利用 VC + + 环境的调试和诊断功能,检查和查找常见的内存缺陷。
了解 VC + + 环境中常见的内存缺陷和症状可以帮助我们减少问题的发生并及时纠正它们。
从错误表现的角度来看,与堆栈相关的错误主要分为两类: 堆栈溢出和函数返回信息被破坏。
(1)堆叠溢出(溢出)
这种类型的错误主要有两种情况:
1)局部变量太大。默认情况下,Windows 为每个线程保留1m 的堆栈空间。在菜单 Project-> Properties-> Configuration Properties-> Linker-> System 中,您可以看到 Stack Reserve Size 选项可以调整保留的堆栈空间大小。
2)递归调用的次数太多。在调试过程中,可以在调用堆栈窗口中找到函数递归调用的模式。
(2)函数返回信息被破坏。
这类错误主要有两种情况:
1)对局部变量的写操作超出范围(溢出)。在调试过程中,函数堆栈被破坏的明显标志是无法显示调用堆栈,并且错误发生在被调用函数即将返回的位置。
2)如果调用函数和被调用函数之间的函数参数不匹配或调用规范不一致。
为了检查这些错误,在编译代码时应该打开/GS/RTCs 开关(在菜单 Project-> Properties-> Configuration Properties-> c/c + +-> Code Generation 下设置)。
另一类错误是动态内存错误。典型情况如下:
(1)内存写出界限。在调试版本中,如果你写一个溢出,你会收到一个跟踪消息“ Damage: after block...”,如果你写一个溢出,你会收到一个跟踪消息“ Damage: before block...”。
(2)删除非法指针。在调试版本中,当删除未初始化的指针或非堆指针时,您将收到 _ crtisvalidheapter 断言错误。
(3)多次发行。在调试版本中,如果多次删除同一个指针,则会收到 _block _ type _ is _ valid 断言错误。若要防止此类错误,应在删除指向动态内存的指针后立即将其设置为 null。
4. 使用 Windows 结构化异常处理机制来处理已发布版本软件的内存崩溃
在程序的发布阶段,程序错误,特别是内存崩溃,应该最小化。如果程序崩溃,您应该优雅地退出,并在程序崩溃时尝试收集正在运行的信息,以帮助程序供应商进行后续调试。为了捕获非法内存访问,了解非法访问指令地址、寄存器内容和其他信息,需要使用 Windows [6]的结构化异常处理(Structured Exception Handling,SEH)机制。MiniDumpWriteDump 是 dbghelp.dll (参见 MSDN)提供的一个 API 函数,用于转储用户模式程序的某些信息(如堆栈状态)并将其保存为文件(如。Dmp 档案)。这个文件可以被微软的调试器(VC + + 或者 WinDBG)用于后期调试。要使用这个函数,需要 dbghelp.h、 dbghelp.lib 和 dbghelp.dll (这些文件可以在 Windows Platform SDK 中找到)。方法调试代码。Dmp 文件之后,您需要为软件的发布版本生成调试符号(pdb)文件(打开编译器/DEBUG 选项)。在得到。Dmp 文件,打开。Dmp 文件与 VC + + ,然后调试和执行它(按 F5)。这样,崩溃场景就会重新出现。文献[5]基于上述方法实现了一个碰撞报告系统。
5结论
实践证明,上述方法和工具所支持的减少软件内存缺陷的方法和工具能有效地防止和发现代码中的内存错误和内存泄漏,并能与开发人员的日常编码无缝结合,执行效率高。上述方法与单元测试、代码评审、日常构建、缺陷跟踪等措施相结合,形成了一个有效的质量保证过程,在我国大型平台软件开发过程中发挥了重要作用。
参考
1 Sutter H, Alexandrescu A. C++ Coding Standards: 101 Rules, Guidelines, and Best Practices[M]. Addison-Wesley Professional, 2004-10.
2 Stroustrup B. The Design and Evolution of C++[M]. Addison-Wesley Professional, 1994-03.
3 Karlsson B. Beyond the C++ Standard Library: An Introduction to Boost[M]. Addison-Wesley Professional, 2005-08.
4 Zyzyck J. A Report Generator for PC-Lint[J]. Dr. Dobb's Journal, 2003, 28(2): 52.
5 Dietrich H. XCrash Report: Exception Handling and Crash Reporting[Z]. 2003-10. http://www.codeproject.com/debug/XCrash ReportPt4.asp.
6 Richter J M. Programming Applications for Microsoft Windows[M]. Microsoft Press, 1999-09.
文本来源
Methods and tools to reduce memory defects in C++ coding - Fire Heart