【问题标题】:_DEBUG vs NDEBUG_DEBUG 与 NDEBUG
【发布时间】:2011-01-18 10:20:04
【问题描述】:

应该使用哪个预处理器定义来指定代码的调试部分?

使用#ifdef _DEBUG#ifndef NDEBUG 或者有更好的方法,例如#define MY_DEBUG?

我认为_DEBUG 是 Visual Studio 特定的,是 NDEBUG 标准吗?

【问题讨论】:

    标签: c++ c debugging


    【解决方案1】:

    Visual Studio 定义 _DEBUG 当您指定 /MTd/MDd 选项时,NDEBUG 禁用标准 C 断言。在适当的时候使用它们,即_DEBUG,如果你希望你的调试代码与MS CRT debugging techniques一致,NDEBUG如果你想与assert()一致。

    如果您定义自己的调试宏(并且您没有破解编译器或 C 运行时),请避免以下划线开头的名称,因为这些是保留的。

    【讨论】:

    • +1。 NDEBUG 特别是允许在单个 TU 中进行#undef'd 和 #define'd (并且重新包含 会相应地更改断言宏)。因为这与预期/期望的不同,所以通常使用另一个宏来控制“编译范围”调试标志,正如这个答案所说。
    • 显然,是编译器本身定义了这些宏,而不是 Visual Studio(IDE)。非常感谢这个答案!
    • 在使用 Windows Driver Kit 8.1 中的应用程序模板时也未定义 NDEBUG。在这种情况下,您可能需要依赖_DEBUG。
    【解决方案2】:

    NDEBUG 标准吗?

    是的,它是一个标准宏,具有 C89、C99、C++98、C++2003、C++2011、C++2014 标准的语义“非调试”。标准中没有 _DEBUG 宏。

    C++2003 标准在“17.4.2.1 Headers”的“page 326”发送阅读器 到标准C。

    NDEBUG 类似于 This is the same as the Standard C library。

    在 C89(C 程序员将此标准称为标准 C)中的“4.2 DIAGNOSTICS”部分中这样说

    http://port70.net/~nsz/c/c89/c89-draft.html

    如果 NDEBUG 在源文件中被定义为宏名 包含在哪里,断言宏被简单地定义为

         #define assert(ignore) ((void)0)
    

    如果看Visual Studio中_DEBUG宏的含义 https://msdn.microsoft.com/en-us/library/b0084kay.aspx 然后会看到,这个宏是由您选择的语言运行时库版本自动定义的。

    【讨论】:

      【解决方案3】:

      我依赖NDEBUG,因为它是唯一一个其行为跨编译器和实现标准化的工具(请参阅标准断言宏的文档)。否定逻辑是一个小的可读性减速带,但它是您可以快速适应的常见习语。

      依赖_DEBUG 之类的东西将依赖于特定编译器和库实现的实现细节。其他编译器可能会也可能不会选择相同的约定。

      第三种选择是为你的项目定义自己的宏,这是相当合理的。拥有自己的宏为您提供了跨实现的可移植性,它允许您独立于断言启​​用或禁用调试代码。不过,总的来说,我建议不要在编译时启用不同类别的调试信息,因为它会导致您必须构建(和测试)的配置数量增加,而收益可能很小。

      使用这些选项中的任何一个,如果您在项目中使用第三方代码,则必须知道它使用哪种约定。

      【讨论】:

      • #if !defined(NDEBUG) #if 不是 #ifdef?
      • 通过@BobStein 更正的原始评论:“我为(稍微)降低可读性而采取的措施“减速带”是在谈论无调试条件时使用#ifdef NDEBUG ...但是要特别注意 #if !defined(NDEBUG) 的否定逻辑。否则很难捕捉到 #ifndef NDEBUG 中的 n"
      【解决方案4】:

      NDEBUG 控制 assert() 语句是否处于活动状态。

      在我看来,这与任何其他调试都是分开的——所以我使用NDEBUG 以外的东西来控制程序中的调试信息。我使用的内容会有所不同,具体取决于我正在使用的框架;不同的系统有不同的启用宏,我使用合适的。

      如果没有框架,我会使用没有前导下划线的名称;这些往往保留给“实现”,我尽量避免出现名称冲突的问题——当名称是宏时更是如此。

      【讨论】:

      • 你能解释一下为什么你认为断言不同于其他类型的调试吗?在什么情况下你会想要一个而不是另一个?
      • 即使我没有将其他调试或跟踪工具编译到代码中,我也会保持断言处于活动状态。断言识别不可能的情况。一般跟踪和调试与 IMO 完全分开。
      • @AdrianMcCarthy:如果您启用了所有调试,它可能会导致程序运行缓慢。因此,对调试类型进行分类并允许人们分别启用/禁用它们是很常见的。 MSVC 有两个或三个用于启用/禁用标准库的各种调试,例如 std::vector。
      • @MooingDuck:同意,但要区分“调试可用并启用”、“调试可用但禁用”和“调试不可用”。可用/不可用决定是编译时问题(在 C 或 C++ 代码中),启用/禁用是调试可用时的运行时决定。只有最原始的调试系统才能使用“可用手段启用”模式。 (也就是说,粗略的可能是有效的,并且在野外有大量粗略但足够有效的调试系统!)
      • @MooingDuck:是的,一些调试代码可能会变慢。在这种情况下,您可能需要一种方法来控制慢速代码是否独立于琐碎检查运行。但这与将断言放在一类中而将#if 控制的代码放在另一类中不同。 assert(huge_object.IsValid()); 可能会很慢,而 assert(ptr != nullptr); 可能不会。我同意 Jonathan 的观点,即 loggingtracing 可能应该不同于断言,至少在大型项目中是这样,但我不认为 loggingtracing 作为调试代码,这就是我要求澄清的原因。
      【解决方案5】:

      保持一致,不管是哪一个。此外,如果由于某种原因您必须使用某个 DEBUG 标识符与另一个程序或工具进行互操作,这很容易做到

      #ifdef THEIRDEBUG
      #define MYDEBUG
      #endif //and vice-versa
      

      【讨论】:

        【解决方案6】:

        不幸的是DEBUG 严重超载。例如,建议始终为 RELEASE 构建生成并保存 pdb 文件。这意味着-Zx 标志和-DEBUG 链接器选项之一。而_DEBUG 与运行时库的特殊调试版本有关,例如对mallocfree 的调用。然后NDEBUG 将禁用断言。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2010-09-28
          • 1970-01-01
          • 1970-01-01
          • 2015-05-29
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多