【译文】 不同调试技术介绍

原文地址: http://oopweb.com/CPP/Documents/DebugCPP/Volume/techniques.html

这一部分介绍调试技术,包含了从阅读手册到使用工具的相关信息。

使用编译器的特性
  一个好的编译器能够对你的代码做很多静态分析,该分析能够检查很多语义错误,比如类型不匹配、死代码等。对于GCC而言,有很多选项影响GCC静态分析做什么和展示什么。它主要有下面两类参数:

Warning options  : GCC有很多warning 标志,很多是 “-Wphrase”这样的形式。你可以选择你感兴趣的添加到Makefile中(如果使用隐式规则,可以设置CFLAGS变量)。注意-Wall 只是包含了程序员们认为在多数情况想有用的选项。

Optimisation flags : GCC可以设置不同的优化等级。其中一些可以触发对代码的流分析。通常情况下,建议使用-O2级别。

   GCC 部分 http://oopweb.com/CPP/Documents/DebugCPP/Volume/techniques.html 

RTFM技术

RTFM是Read The Fine Manual的缩写。你要保证花时间找了该任务相关的文档,比如工具(不只是编译器,还有make,预处理器和链接器)、库、算法的文档。通常你不需要知道文档的所用事情,但是你必须知道这些文档是干什么的,都是什么目的。 你应当能够区分教程和参考文档:
  教程只是使用示例的方法来教你怎么用。在一个教程中,传递idea 比咬文嚼字的信息重要!
      参考文档假设你已经熟悉了它的topic,现在你需要找一个特定问题的确切答案。好的参考文档是非常详尽的,通过元信息(内容目录,索引,交叉引用)使你能够快速找到想要的答案。在线超文本是参考文档的一个很方便的格式。

printf() debugging
这是我们最经常碰到的形式,我们使用printf打印信息来了解控制流和变量当时的值。但是这样做有很多弊端:

1代码只是暂时加进去的,当前bug一旦解决就要删除。遇到下一bug,你又要添加相似的代码。你马上将看到有更好的方法添加调试信息。

2这样做干扰了程序的正常输出,并且降低了程序的运行速度。

3更重要的,它通常是没有帮助的。输出到stdout的信息是被缓存的,程序一旦crash,这些信息就丢失了。因此,你很可能错过了重要的信息,误导你到错误的地方寻找bug。
如果你考虑使用pringf调试,下面是几个建议:

1把错误输出到stderr,stderr是没有缓存的,这样就避免了crash时信息的丢失

2不要直接使用printf,定义一个宏使用,这样你就可以很轻松的打开或者关闭调试代码

3使用debugging level来管理调试信息

下面是一个很好的实例:

#ifndef DEBUG_H
#define DEBUG_H
#include <stdarg.h>

#if defined(NDEBUG) && defined(__GNUC__)
/* gcc's cpp has extensions; it allows for macros with a variable number of
   arguments. We use this extension here to preprocess pmesg away. */
#define pmesg(level, format, args...) ((void)0)
#else
void pmesg(int level, char *format, ...);
/* print a message, if it is considered significant enough.
      Adapted from [K&R2], p. 174 */
#endif

#endif /* DEBUG_H */
        
File debug.c:

#include "debug.h"
#include <stdio.h>

extern int msglevel; /* the higher, the more messages... */

#if defined(NDEBUG) && defined(__GNUC__)
/* Nothing. pmesg has been "defined away" in debug.h already. */
#else
void pmesg(int level, char* format, ...) {
#ifdef NDEBUG
    /* Empty body, so a good compiler will optimise calls
       to pmesg away */
#else
        va_list args;

        if (level>msglevel)
                return;

        va_start(args, format);
        vfprintf(stderr, format, args);
        va_end(args);
#endif /* NDEBUG */
#endif /* NDEBUG && __GNUC__ */
}

断言 :防守式编程
代码中有很多条件判断,对于函数,有前置条件和后置条件。使用assert对这些添加判断,保证程序的正确。 asset是一个宏,你可以在编译的时候使用-DNDEBUG选项来关闭。

ANWB 调试
‘ANWB Debugging’是基于一个简单的原则: 学习东西的最好的方式是教授它们。
你找到一个,最好是不相关的,旁观者,向她解释你的代码是如何工作的。这迫使你重新思考你的假设,解释究竟发生了什么事,很多时候通过这种方式找到你的问题的原因。

原文地址:https://www.cnblogs.com/ridox/p/4365053.html