技术开发 频道

调试策略、技巧及陷阱

【IT168 技术文章】

  使用合适的工具

  如果你正寻找分段错误,你就会想用调试器。如果你正要处理奇怪的内存问题(或难以诊断的分段错误),那么在Linux中使用Valgrind或在Windows中使用Purify吧。

  调试问题

  有时候我们想出一个解决方案后却发现方案很难执行。事实上,换个角度问题或许可以更明朗。当我看到有人竭力调试一个复杂的大规模的代码时,我首先想到的是问问自己是否有更简便的办法。通常情况下,一旦你的代码写得很糟糕,你反而能清楚意识到好的代码应是怎样的。请记住,不能因为代码是你写的就要保留它!

  诀窍是,明白你是要解决初始问题,还是要解决某一特定方案。如果是解决方案,那么你的问题就可能根本不是由初始问题造成,也许你过虑了或者尝试了一种错误的做法。例如,我最近需要分析文件并输入一些数据到Access数据库以还原分析工具。我的第一反应是编写一个与Access直接对接的Ruby脚本,使用SQL查询向数据库输入所有数据。由于我审视了Ruby可以为此提供的支持,我很快意识到对这个问题的“解决办法”要比理论上的久得多。于是我掉转方向,写了个只是输出一个逗号分隔值文件的脚本,如此,我大约花了一小时完成数据输入。

  关于错误代码

  人们往往不愿抛出他们已经写了和重写过的错误码。原因之一是,写完的代码标志着工作的完成,再提出来会觉得走了回头路。但是在你调试时,重写代码似乎更具吸引力,因为你可能通过重写代码节省了调试的时间。诀窍在于,移除不良代码不需要重写整个程序(除非它无可救药),只需要重写有问题的部分。

  你的第二稿可能更清洁且漏洞更少,你也可以避免重写代码等问题,这样你能说清楚它的工作原理。

  另一方面,当你绝对相信,看起来可怕的代码是要使用的正确代码时,你会想用你的理由为其注释,以防某人(或自己)犯同样的错。

  避免“复制/粘贴”带来的错误

  每当你复制并粘贴大量代码时,你都留给了自己一大堆麻烦。即便你还没有调试,你也将不得不这样做。如果你忘了在什么地方复制过代码,你很可能将重复调试相同的代码。

  避免复制/粘贴综合症的最好方法是尽可能使用函数概括重复代码。有些东西在C + +中不容易避免,无论正在做什么你都会为循环写很多代码,所以你不能提取整个循环过程。但是,如果在多个地方存在同样的循环本体,就可能是应该退出到单独函数的一个信号。作为奖励,代码变得更简单,你也可以重复使用这一函数,而不必找到一大堆代码去复制。

  何时复制代码

  虽然复制代码通常是危险的,但有些时候它可能是最好的选择。例如,如果您需要对一大段代码进行缩减和不规则调整,其余部分保持不变,那么复制,粘贴和仔细编辑是可以的。通过复制代码可以避免因打字有误而带来的新错误。

  早期测试

  退出代码并把它变成函数的优点之一是,你可以分别测试这些函数。这意味着你可以避免调试由原函数中的简单错误引发的大问题。这样的单元测试需要一定的训练和对错误代码有良好的判断力。

  早期测试的另一个优点是你会更关心你具体界面的类别。如果因为你使用的是断言而不是一个例外或错误代码而无法测试错误操作,这表明,你应该使用某种形式的错误报告,而不是断言。如果你的接口类别不灵敏,或你的函数有着无法被理解,更不用说被记住的自变量。那么是时候在编码前重新考虑一下。

  编译器警告

  编译器可以发现许多潜在的错误。一些这样的错误包括使用未初始化变量,在条件句,或C + +中意外将检查平等转让,与混合类型相关的错误,如指针和绑定约束。

  格式输出谎言

  由于操作系统通常缓冲I/O,所以在调试过程中使用格式输出是危险的。可能的话,使用调试器找出有问题的代码行,而不是缩小被格式输出和进位输出弄乱的代码。

  清除输出

  有些时候你要在日志文件中跟踪一些情况,需要从程序启动到出现错误时的所有数据。为了确保你收集了所有数据,一定要清除它:你可以用C 刷新缓冲区,或用C++输出endl 。 fflush把你要写入的文件指针的错误清除。

  检查辅助函数

  人们很容易激动时忘记这点:总是确认您的辅助函数运行正常,特别是在看似简单的代码缺失时。如果可能的话,先分离每个辅助函数并对它们进行单独测试,然后再进行代码测试。

  当原因不能立刻生效时

  即使辅助函数不是导致问题的直接原因,其副作用也可能导致潜在问题。举例来说,如果你有一个可以返回NULL的辅助函数,且你把它的输出传到处理C 字符串的库函数,你会看到直接原因是因为解除了NULL指针关联,但真正的原因是因为你写了问题函数(或是你没有在调用NULL后进行检查)。

  代码可以多处使用

  调试时的另一个问题是,你发现的问题由某种特别的函数调用引起,在那个函数里设置一个断点,然后会发现,在整个代码中对同一个函数有数百次调用。

  最显而易见的办法是在冲击断点后检查调用栈,或在出错调用前有效设置断点。不幸的是,如果同样的调用出现1000次但在1001次时失败了,这就不一定起作用了。可能的解决方案包括清点函数调用次数,然后逐步通过设置在函数中的断点,或使用一个静态变量作为计数器。

 

0
相关文章