为什么要断言一个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在某些情况下它们有不良效果。

  1. 它允许捕获文件(通过__FILE__ )和行号(通过__LINE__
  2. 它允许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模式编译。