为什么断言宏而不是函数?

时间:2014-08-13 11:58:15

标签: c assert c-preprocessor

我的讲师在课堂上问过我,我想知道为什么它是一个宏而不是一个函数?

5 个答案:

答案 0 :(得分:70)

简单的解释是标准要求assert成为一个宏,如果我们查看draft C99 standard,据我所知,这些部分在{{{ 3}}以及)部分7.2 诊断 2 说:

  

断言宏应实现为宏,而不是实际宏   功能。如果禁止宏定义以访问   实际功能,行为未定义。

为什么需要这个,draft C11 standard中给出的理由是:

  

使断言成为真正的函数可能很困难或不可能,因此它仅限于宏   形式。

这不是很有用,但我们可以从其他要求中看出原因。回到7.2 1 部分说:

  

[...]如果NDEBUG被定义为源文件中的宏名称   在包含的地方,断言宏被简单地定义为

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

根据NDEBUG的当前状态重新定义断言宏   每次都包括在内。

这很重要,因为它允许我们在发布模式下关闭断言的简单方法,您可能需要花费可能昂贵的支票。

并且第二个重要要求是需要使用宏__FILE____LINE____func__,这将在7.2.1.1 部分中介绍宏说:

  

[...]断言宏写入有关特定呼叫的信息   失败的后者分别是后者的价值观   预处理宏__FILE_ _和__LINE_ _以及标识符   __func_ _)在标准错误流中以实现定义的格式。 165)然后调用中止函数。

脚注165说:

  

写入的信息可能是以下形式:

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

将它作为宏允许在正确的位置评估宏__FILE__等......并且Joachim指出是宏允许它插入原始的表达式它产生的信息。

C ++标准草案要求cassert标题的内容与Standrd C库的assert.h标题相同:

  

内容与标准C库标题相同。

     
    

参见:ISO C 7.2。

  

为什么(无效)0?

为什么使用(void)0而不是其他什么表达式?我们可以提出几个原因,首先是断言概要在7.2.1.1部分中的含义:

void assert(scalar expression);

它说(强调我的):

  

断言宏将诊断测试放入程序中; 扩展为无效表达。

表达式(void)0与最终得到 void表达式的需要一致。

假设我们没有这个要求,其他可能的表达式可能会产生不良影响,例如允许在发布模式下使用assert,这在调试模式下是不允许的,例如使用Rationale for International Standard—Programming Languages—C将允许我们在分配中使用assert,如果使用正确,可能会生成expression result unused警告。至于使用复合语句作为评论建议,我们可以从plain 0看到它们在某些情况下会产生不良影响。

答案 1 :(得分:45)

  1. 它允许捕获文件(通过__FILE__)和行号(通过__LINE__
  2. 允许assert替换在发布模式下构建时无效(即((void)0))的有效表达式

答案 2 :(得分:13)

如果在包含时已经定义了名为NDEBUG的宏,则禁用此宏。这允许编码器在调试程序时在源代码中包含所需数量的断言调用,然后通过简单地包括如下行来禁用生产版本的所有调用:

#define NDEBUG 

在其代码的开头,包含<assert.h>之前。

因此,此宏旨在捕获编程错误,而不是用户或运行时错误,因为它通常在程序退出调试阶段后被禁用。


将其作为函数将增加一些函数调用,并且您无法在释放模式下控制所有此类断言。

如果您使用函数,则_FILE____LINE____func__将给出该断言函数的代码值。不是呼叫线路或呼叫功能线路。

答案 3 :(得分:13)

一些断言调用起来可能很昂贵。您刚刚编写了一个高性能矩阵反转例程,并添加了一个完整性检查

assert(is_identity(matrix * inverse))
到最后。好吧,你的矩阵非常大,如果assert是一个函数,那么在将它传递给assert之前需要花费大量的时间来进行计算。如果您没有进行调试,那么您真的不想花时间。

或许断言相对便宜,但它包含在一个非常短的函数中,将在内循环中调用。或其他类似的情况。

通过改为使用assert宏,可以在关闭断言时完全取消计算。

答案 4 :(得分:1)

  

为什么断言宏而不是函数?

因为它应该以 DEBUG 模式编译,不应该以 RELEASE 模式编译。