为什么`assert`宏即使具有`NDEBUG`仍具有价值?

时间:2019-05-23 04:49:11

标签: c++ c

环境:

$ g++ --version
g++ (Ubuntu 7.4.0-1ubuntu1~18.04) 7.4.0

众所周知,可以通过在包含assertNDEBUG)之前定义的assert.h来禁用用于调试的类函数宏cassert。但是,在我的环境中阅读/usr/include/assert.h后,我发现了下面的代码。

#if defined __cplusplus && __GNUC_PREREQ (2,95)
# define __ASSERT_VOID_CAST static_cast<void>
#else
# define __ASSERT_VOID_CAST (void)
#endif

#ifdef  NDEBUG

# define assert(expr)       (__ASSERT_VOID_CAST (0))

# ifdef __USE_GNU
#  define assert_perror(errnum) (__ASSERT_VOID_CAST (0))
# endif

#else /* Not NDEBUG.  */

因此,即使使用NDEBUGassert也会扩展到某个值,因此对性能的影响不大,但可以肯定地(如果在预处理步骤之后未进行优化)。由于C ++标准允许类似函数的宏带有空值,因此

#ifdef NDEBUG
#define assert(expr)
#endif

将是一个很好的实现。

有什么理由应该选择非空值?

2 个答案:

答案 0 :(得分:6)

在定义NDEBUG时宏具有值的一个原因是因为C标准要求它具有一个-甚至规定了它应该是什么。

C11

  

§7.2 Diagnostics <assert.h>

     

¶1标头<assert.h>定义了assertstatic_assert宏,并引用了另一个宏,

    NDEBUG
     未由<assert.h>定义的

。如果在源文件中包含NDEBUG的位置将<assert.h>定义为宏名,则将assert宏定义为

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

每次包含assert时,都会根据NDEBUG的当前状态重新定义<assert.h>宏。

     

¶2assert宏应实现为宏,而不是实际功能。如果取消了宏定义以访问实际功能,则该行为未定义。

     

...

     

7.2.1.1 The assert macro

     

简介

    #include <assert.h>
    void assert(scalar expression);
     

说明

     

¶2assert宏将诊断测试放入程序中。它扩展为void表达式。执行时,如果expression(应为标量类型)为false(即比较等于0),则assert宏将写入有关失败的特定调用的信息(包括文本)参数,源文件的名称,源行号和封闭函数的名称-后者分别是预处理宏__FILE____LINE__的值以及标识符__func__)上的标准错误流以实现定义的格式。 191)然后调用abort函数。

     

返回

     

¶3assert宏不返回任何值。

     

191)所写消息的形式可能是:

Assertion failed: expression, function abc, file xyz, line nnn.

C ++ 11

C ++ 11标准说(§19.3断言)说:

  

头文件<cassert>…提供了一个宏,用于记录C ++程序声明,并提供了一种禁用声明检查的机制。

     

...

     

内容与标准C库头文件<assert.h>相同。

因此,相同的规则适用于C ++和C。C++标准未明确指出C ++ static_assert和C _Static_assert之间的区别(以及{{1}的定义}作为static_assert标头的C版本中的_Static_assert。最终结果还是差不多。

答案 1 :(得分:1)

要使用NDEBUG,必须在#define NDEBUG之前写#include <cassert>。如果要跳过assert(expression)语句,请编写#define NDEBUG或用其他注释方式。

https://en.cppreference.com/w/c/error/assert

相关问题