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

ynn*_*ynn 8 c c++

环境:

$ g++ --version
g++ (Ubuntu 7.4.0-1ubuntu1~18.04) 7.4.0
Run Code Online (Sandbox Code Playgroud)

众所周知,assert可以NDEBUG在包含assert.hcassert)之前通过定义禁用用于调试的类函数宏。但是,/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.  */
Run Code Online (Sandbox Code Playgroud)

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

#ifdef NDEBUG
#define assert(expr)
#endif
Run Code Online (Sandbox Code Playgroud)

将是一个很好的实现。

有什么理由可以选择非空值?

Jon*_*ler 6

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

C11

§7.2诊断 <assert.h>

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

    NDEBUG
Run Code Online (Sandbox Code Playgroud)

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

    #define assert(ignore) ((void)0)
Run Code Online (Sandbox Code Playgroud)

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

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

7.2.1.1 assert

概要

    #include <assert.h>
    void assert(scalar expression);
Run Code Online (Sandbox Code Playgroud)

描述

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

退货

¶3 assert宏不返回任何值。

191)编写的消息可能具有以下形式:

Assertion failed: expression, function abc, file xyz, line nnn.
Run Code Online (Sandbox Code Playgroud)

C ++ 11

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

标题<cassert>…提供了用于记录C ++程序断言的宏以及用于禁用断言检查的机制。

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

因此,相同的规则适用于C ++和C。C++标准未明确指出C ++ static_assert与C 之间的区别_Static_assert(以及标头C版本中static_assertas 的定义)。但是,最终结果大致相同。_Static_assert<assert.h>