_DEBUG和NDEBUG


142

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

使用#ifdef _DEBUGor #ifndef NDEBUG或有更好的方法来做到这一点,例如#define MY_DEBUG

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

Answers:


113

_DEBUG当您指定/MTdor /MDd选项时,Visual Studio定义NDEBUG禁用标准C断言。在适当的时候使用它们,也就是说_DEBUG,如果你希望你的调试代码必须与一致MS CRT调试技术NDEBUG如果你想与其保持一致assert()

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


19
+1。尤其是允许在单个TU中对NDEBUG进行#undef'd和#define'd(并包括<assert.h>相应地更改assert宏)。因为这与预期的/期望的不同,所以通常使用另一个宏来控制“编译范围内”的调试标志,如答案所示。

1
显然,定义这些宏的是编译器本身,而不是Visual Studio(IDE)。非常感谢您的回答!
Niklas R

当使用Windows Driver Kit 8.1中的应用程序模板时,也没有定义NDEBUG。在这种情况下,您可能需要依赖_DEBUG。
navossoc 2016年

53

NDEBUG是标准的吗?

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

C ++ 2003标准将阅读器的“ 17.4.2.1标头”中的“页326”发送给标准C。

NDEBUG类似于。这与标准C库相同。

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

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

如果在源文件中包含NDEBUG的位置将其定义为宏名,则assert宏的定义如下:

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

如果看一下_DEBUGVisual Studio https://msdn.microsoft.com/zh-cn/library/b0084kay.aspx中的宏 的含义, 就会看到该宏是由您的语言运行库版本选择的。


50

我依赖NDEBUG,因为它是唯一一种行为已在整个编译器和实现之间标准化的行为(请参阅有关标准assert宏的文档)。否定逻辑是较小的可读性,但它是您可以快速适应的常见用法。

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

第三种选择是为项目定义自己的宏,这是很合理的。拥有自己的宏可为您实现各种实现之间的可移植性,并使您可以独立于断言而启用或禁用调试代码。尽管总的来说,我还是建议不要在编译时启用不同类型的调试信息,因为这会导致您不得不构建(和测试)的配置数量增加,从而带来的好处可能很小。

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


1
#if !defined(NDEBUG)<-@HostileFork不是您的意思吗?#if不是#ifdef
鲍勃·斯坦

1
通过@BobStein纠正的原始评论:“我已经采取了(略微)减轻可读性“ speedbump”的方法是,在谈论无调试条件时使用#ifdef NDEBUG...,但是请特别注意负逻辑#if !defined(NDEBUG)。否则,它是一个有点难以赶上在N #ifndef NDEBUG
HostileFork表示不信任SE

17

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

在我看来,这与任何其他调试都是分开的-因此,我使用了除NDEBUG控制程序中调试信息以外的方法。根据使用的框架,我使用的工具会有所不同。不同的系统具有不同的启用宏,因此我将使用适当的东西。

如果没有框架,我会使用没有下划线的名称。那些往往是为“实现”保留的,我尽力避免名称冲突的问题-当名称是宏时,双倍地出现。


您能否解释为什么您认为断言与其他调试类型不同?在什么情况下,您要一个而不要另一个?
Adrian McCarthy

4
即使不将其他调试或跟踪功能编译到代码中,我也会保持活动状态。断言确定不可能的情况。常规跟踪和调试与IMO完全分开。
Jonathan Leffler

2
@AdrianMcCarthy:如果启用了所有调试,则可能导致程序运行缓慢。因此,对调试类型进行分类并允许人们分别启用/禁用它们是很常见的。MSVC有两个或三个,它们用于启用/禁用标准库的各种调试,例如std :: vector。
Mooing Duck,

@MooingDuck:同意,但是要区分“调试可用并启用”和“调试可用但已禁用”和“调试不可用”。可用/不可用的决定是编译时问题(使用C或C ++代码),并且当调试可用时,启用/禁用则是运行时决定。只有最原始的调试系统才能使用“可用的均值启用”模式。(也就是说,原油可能是有效的,并且有很多原油-但足够有效-在野外调试系统!)
Jonathan Leffler 2013年

2
@MooingDuck:是的,某些调试代码的变化速度可能很慢。在这种情况下,您可能需要一种方法来控制慢速代码是否独立于琐碎的检查而运行。但这与将断言放在一个类别中,而将受#if控制的代码放在另一类别中是不同的。 assert(huge_object.IsValid());可能会很慢,而assert(ptr != nullptr);事实并非如此。我同意Jonathan的观点,至少在较大的项目中,日志记录跟踪可能与断言不同,但是我不认为日志记录跟踪是调试代码,这就是为什么要求澄清的原因。
Adrian McCarthy

6

保持一致,哪一个都没关系。另外,如果由于某种原因必须使用特定的DEBUG标识符与另一个程序或工具进行互操作,则很容易做到

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

4

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.