Fresh Energy

           Nerver say die!

IT博客 首页 新随笔 联系 聚合 管理
  84 Posts :: 0 Stories :: 197 Comments :: 0 Trackbacks
英文原文:http://www.ibm.com/developerworks/aix/library/au-memorytechniques.html?S_TACT=105AGX52&S_CMP=cn-a-au
翻译文章:http://www.ibm.com/developerworks/cn/aix/library/au-memorytechniques.html
不过我觉得他的翻译有些地方怪怪的,如果直接看英文清楚明白。下面有些词根据我的理解改了点表达方式。

这篇文章主要讲解如下四种问题
  • 内存泄漏
  • 错误分配,包括 free() 释放内存问题和未初始化的引用
  • 悬挂指针
  • 数组越界
  • 内存泄漏:举两个例子,例如在一个函数中为一变量malloc(100)分配了100字节,却忘记free(),那么每次调用这个函数都将泄漏100字节,一两次可能对现代的内存微不足道,但是连续多次后,问题就会变得严重。除了malloc或new的问题,文件句柄也可能引起问题。又如在fopen然后对文件进行操作之后,没有相应的fclose就容易引发错误。其他资源(如信号量、网络句柄、数据库连接等)同样值得考虑。

    内存错误分配:例如指针未初始化,int *p2;           /* Uh-oh!  No one has initialized p2. */         *p2 = datum;
    (这里我不是很明白,虽然没初始化,但声明完就直接用有什么问题?)。又如,内存分配和释放问题。malloc了一次,却free了两次,或只声明了指针没有malloc却free。这些错误通常也不太严重。尽管 C 标准在这些情形中没有定义具体行为,但典型的实现将忽略错误,或者快速而明确地对它们进行标记;

    悬挂指针:当程序员在内存资源释放后再使用资源时会发生悬挂指针错误,例如执行free之后再使用此指针就会出问题。传统的调试很难找出悬挂指针引起的错误,由于下面两个明显原因,它们很难再现: 即使影响提前释放内存范围的代码已本地化,内存的使用仍然可能取决于应用程序甚至(在极端情况下)不同进程中的其他执行位置。 悬空指针可能发生在以微妙方式使用内存的代码中。结果是,即使内存在释放后立即被覆盖,并且新指向的值不同于预期值,也很难识别出新值是错误值。

    数组越界:回头看一下内存泄漏的程序例子:如果 explanation 的长度超过 80,则会发生什么情况?回答:难以预料,但是它可能与良好情形相差甚远。特别是,C 复制一个字符串,该字符串不适于为它分配的 100 个字符。在任何常规实现中,“超过的”字符会覆盖内存中的其他数据。内存中数据分配的布局非常复杂并且难以再现,所以任何症状都不可能追溯到源代码级别的具体错误。

    管好内存的良好编程策略

    编码风格: 影响资源(特别是内存)的函数和方法需要显式地解释本身,进行必要的注释。

    可以使用各种方法解决内存问题:

    • 专用库
    • 语言
    • 软件工具
    • 硬件检查器

    在这整个领域中,我始终认为最有用并且投资回报率最大的是考虑改进源代码的风格。它不需要昂贵的代价或严格的形式;可以始终取消与内存无关的段的注释,但影响内存的定义当然需要显式注释。添加几个简单的单词可使内存结果更清楚,并且内存编程会得到改进。

    人工检测
    通过少量的实践和适当的文本搜索,能够快速验证平衡的 *alloc()free() 或者 newdelete 的代码。

    但人工检测代码有时会遇到如下问题,例如malloc或free的执行受条件判断的影响,仔细进行源分析可以从此类条件推理出证实正确的结论。尽管大量发布的内存问题描述都强调工具和语言,对于我来说,最大的收获来自“软的”以开发人员为中心的流程变更。您在风格和检测上所做的任何改进都可以帮助您理解由自动化工具产生的诊断。

    静态的自动语法分析

    静态语法分析是 lintstrict compilation 和几种商业产品的工作:扫描编译器接受的源文本和目标项,找出可能的错误。尽管 lint 已过时,并有一定的局限性,但是,没有使用它(或其较高级的后代)的许多程序员犯了很大的错误。通常情况下,您能够编写忽略 lint 的优秀的专业质量代码,但努力这样做的结果通常会发生重大错误。其中一些错误影响内存的正确性。与让客户首先发现内存错误的代价相比,即使对这种类别的产品支付最昂贵的许可费也失去了意义。现在,即使 lint 标记的编码可能向您提供所需的功能,但很可能存在更简单的方法,该方法可满足 lint,并且比较强键又可移植。

    内存库

    补救方法的最后两个类别与前三个明显不同。前者是轻量级 的;一个人可以容易地理解并实现它们。另一方面,内存库和工具通常具有较高的许可费用,对部分开发人员来说,它们需要进一步完善和调整。有效地使用库和工具的程序员是理解轻量级的静态 方法的人员。可用的库和工具给人的印象很深:其作为组的质量很高。但是,即使最优秀的编程人员也可能会被忽略内存管理基本原则的非常任性的编程人员搅乱。程序员为解决内存问题应先了解一下自己的源。在这完成之后,才去考虑库。使用几个库能够编写常规的 C 或 C++ 代码,并保证改进内存管理。库可以解决多种不同的内存问题,以致于直接对它们进行比较是非常困难的;这方面的常见主题包括垃圾收集智能指针智能容器。大体上说,库可以自动进行较多的内存管理,这样程序员可以犯更少的错误。

    内存工具
    开发真正基于 C 的应用程序的开发团队需要运行时内存工具作为其开发策略的一部分。已介绍的技术很有价值,而且不可或缺。在您亲自尝试使用内存工具之前,其质量和功能您可能还不了解。
    本文主要讨论了基于软件的内存工具。还有硬件内存调试器;在非常特殊的情况下(主要是在使用不支持其他工具的专用主机时)才考虑它们。
    市场上的软件内存工具包括专有工具(如 IBM Rational® Purify 和 Electric Fence)和其他开放源代码工具。其中有许多可以很好地与 AIX 和其他操作系统一起使用。
    所有内存工具的功能基本相同:构建可执行文件的特定版本(很像在编译时通过使用 -g 标记生成的调试版本)、练习相关应用程序和研究由工具自动生成的报告。例如内存越界的报告。

    posted on 2007-09-14 23:02 zhouditty 阅读(619) 评论(2)  编辑 收藏 引用 所属分类: C++程序设计

    Feedback

    # re: 《内存调试技巧》阅读笔记 2007-09-15 21:04 踏雪赤兔
    int *p2; /* Uh-oh! No one has initialized p2. */ *p2 = datum;

    未初始化的指针指向的内存地址可能为0,或者一些随机的值,总之就是不属于你使用的地址,对该地址进行写入会导致access violation  回复  更多评论
      

    # re: 《内存调试技巧》阅读笔记 2007-09-16 23:32 zhouditty
    @踏雪赤兔
    哦,没注意,看错了以为是赋指针,原来是企图对变量赋值  回复  更多评论
      

    只有注册用户登录后才能发表评论。