为什么要断言一个macros而不是函数?
我的讲师在课堂上问我,我想知道为什么它是一个macros而不是一个function?
简单的解释是,如果我们看看C99标准草案 ( 据我所知C11标准草案中的章节也是一样的),标准要求assert
是一个macros。第7.2
节诊断第2段说:
断言macros应该被实现为一个macros而不是一个实际的函数。 如果为了访问一个实际的函数而禁止了macros定义,那么行为是不确定的。
为什么要这样做呢,国际标准程序devise语言C的理由是:
要确定一个真正的function是困难的或不可能的,所以它只限于macros观forms。
这是不是很丰富,但我们可以从其他要求看到为什么。 回到第7.2
节第1段说:
[…]如果将NDEBUG定义为包含源文件中的macros名称,则assertmacros定义为
#define assert(ignore) ((void)0)
每次包含时,根据NDEBUG的当前状态重新定义断言macros。
这很重要,因为它允许我们在发布模式中closures断言的简单方法,您可能想要花费昂贵的检查费用。
第二个重要的要求是需要使用__func__
__FILE__
, __func__
和__func__
macros,这个在7.2.1.1
节中讲述。
[…] assertmacros在标准错误stream上写入有关特定调用失败的信息,后者分别是预处理macros__FILE_ _和__LINE_ _以及标识__func_ _)的值,实现定义的格式。 然后它调用中止函数。
脚注165
说:
所写的信息可能是这样的forms:
Assertion failed: expression, function abc, file xyz, line nnn.
把它作为一个macros允许macros__FILE__
等…在正确的位置进行评估,因为Joachim指出macros是允许它在它生成的消息中插入原始expression式的。
C ++标准草案要求cassert
头的内容与Standrd C库的assert.h
头相同:
内容与标准C库标题相同。
另见:ISO C 7.2。
为什么(无效)0?
为什么使用(void)0
而不是其他的expression方式? 我们可以想出几个理由,首先是7.2.1.1
节中断言摘要的外观:
void assert(scalar expression);
它说( 强调我的 ):
断言macros将诊断testing放入程序中; 它扩大到一个空洞的expression。
expression式(void)0
与需要以voidexpression式结束的expression式一致 。
假设我们没有这个要求,其他可能的expression式可能会产生不良影响,例如允许在debugging模式下不允许使用assert
模式的assert
,例如使用普通的0
将允许我们在一个赋值中使用assert
,以及正确使用可能会产生一个expression result unused
警告。 至于使用复合语句作为注释build议,我们可以从C多行macros看到:do / while(0)vs scope block在某些情况下它们有不良效果。
- 它允许捕获文件(通过
__FILE__
)和行号(通过__LINE__
) - 它允许
assert
替代一个有效的expression式,在释放模式下构build时什么也不做(即((void)0)
)
如果在包含macros名称NDEBUG时已经定义了该macros,则禁用该macros。 这允许编码器在debugging程序时根据需要在源代码中包含尽可能多的断言调用,然后通过简单地包括如下行来禁用所有这些调用程序:
#define NDEBUG
在其代码的开头,包含<assert.h>
。
因此,该macros被devise用来捕获编程错误,而不是用户或运行时错误,因为在程序退出debugging阶段后通常会被禁用。
把它作为函数会增加一些函数调用,并且你不能在释放模式下控制所有的这些断言。
如果你使用函数,那么_FILE__
, _FILE__
和__func__
将给出这个断言函数代码的值。 不是说呼叫线路或呼叫function的线路。
一些断言可能会很昂贵。 您刚刚编写了一个高性能的matrix求逆程序,并添加了一个完整性检查
assert(is_identity(matrix * inverse))
到最后。 那么,你的matrix是相当大的,如果assert
是一个函数,那么在将它传递给assert之前,需要花费很多时间来进行计算。 如果你没有进行debugging,你真的不想花费时间。
或者,也许断言是相对便宜,但它包含在一个非常短的函数,将被调用在内部循环。 或者其他类似的情况。
通过assert
一个macros来代替,当断言被closures时,你可以完全消除这个计算。
为什么要断言一个macros而不是函数?
因为它应该以DEBUG模式编译,不应该以RELEASE模式编译。