_ DEBUG vs NDEBUG

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

使用 #ifdef _DEBUG#ifndef NDEBUG,还是有更好的方法,例如 #define MY_DEBUG

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

186847 次浏览

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

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

VisualStudio 在指定 /MTd/MDd选项时定义 _DEBUGNDEBUG禁用标准 C 断言。在适当的时候使用它们,即如果您希望调试代码与 微软 CRT 调试技术NDEBUG一致,则使用 _DEBUG,如果您希望与 assert()一致。

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

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

在我看来,这是独立于任何其他调试-所以我使用 NDEBUG以外的东西来控制程序中的调试信息。我使用的方法各不相同,这取决于我使用的框架; 不同的系统有不同的启用宏,我使用任何合适的方法。

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

我依赖于 NDEBUG,因为它是唯一一个其行为在编译器和实现之间是标准化的(请参阅标准断言宏的文档)。否定逻辑是一个小的可读性减速器,但它是一个常见的习惯用法,你可以很快适应。

要依赖类似 _DEBUG的东西,就要依赖特定编译器和库实现的实现细节。其他编译器可以选择相同的约定,也可以不选择相同的约定。

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

使用这些选项中的任何一个,如果您使用第三方代码作为项目的一部分,那么您必须了解它使用的约定。

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

NDEBUG 是标准的吗?

是的,它是一个标准的宏,语义“ Not Debug”适用于 C89,C99,C + + 98,C + + 2003,C + + 2011,C + + 2014标准。标准中没有 _DEBUG宏。

C + + 2003标准发送阅读器在“第326页”在“17.4.2.1标题” 标准 C。

该 NDEBUG 与标准 C 库相似。

在 C89(C 程序员称此标准为标准 C)的“4.2 DIAGNOSTICS”部分中提到

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

如果 NDEBUG 定义为源文件中某点的宏名称 在包含 < assert.h > 的地方,断言宏被简单地定义为

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

如果查看 VisualStudio 中 _DEBUG宏的含义 Https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros 然后就会看到,这个宏是由您选择的语言运行库版本自动定义的。

不管名称如何,如果您正在创建调试版本,那么 NDEBUG与此无关,它控制断言(assert())是否处于活动状态。我不会在它的基础上做任何其他事情,因为你可能希望有没有断言的调试构建,或者有时候有断言的发布构建,然后你必须相应地设置 NDEBUG,但这并不意味着你也希望所有其他代码都是调试或发布代码。

从编译器的角度来看,没有调试版本这种东西。你告诉编译器用一组特定的设置来构建代码,如果你想为不同类型的构建使用不同的设置,那么这实际上是你自己编写的代码,而编译器对此一无所知。您实际上可能有50种不同的构建样式,而不仅仅是发布和调试(配置文件、测试、部署等) ,所以这取决于您如何在自己的代码中识别这些样式。如果需要预处理器标记,可以定义这些标记的命名方式,并且与代码中定义的其他所有内容一样,应用相同的名称空间规则。