首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >libc6: assert宏定义中的逗号运算符

libc6: assert宏定义中的逗号运算符
EN

Stack Overflow用户
提问于 2019-05-26 21:52:03
回答 2查看 135关注 0票数 3

我的系统使用libc6 2.29。在/usr/include/assert.h中,我可以找到assert()宏的定义:

代码语言:javascript
复制
/* The first occurrence of EXPR is not evaluated due to the sizeof,
   but will trigger any pedantic warnings masked by the __extension__
   for the second occurrence.  The ternary operator is required to
   support function pointers and bit fields in this context, and to
   suppress the evaluation of variable length arrays.  */
#  define assert(expr)                          \
  ((void) sizeof ((expr) ? 1 : 0), __extension__ ({         \
      if (expr)                             \
        ; /* empty */                           \
      else                              \
        __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION);   \
    }))

我想知道为什么要使用逗号运算符,以及'The first occurrence of EXPR is not evaluated due to the sizeof‘是什么意思。

使用以下定义会有什么问题:

代码语言:javascript
复制
#  define assert(expr)                      \
  ({                                        \
      if (expr)                             \
           ; /* empty */                            \
      else                              \
           __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION);    \
    })

编辑:

如果expr为真,运算符({ })会得到什么值?

是否可以重写assert()的定义,如下所示?

代码语言:javascript
复制
#  define assert(expr)                          \
  ((void) sizeof ((expr) ? 1 : 0), __extension__ ({         \
      if (!expr)                                \
          __assert_fail (#expr, __FILE__, __LINE__, __ASSERT_FUNCTION); \
    }))

最后一个定义的问题在哪里?

EN

回答 2

Stack Overflow用户

发布于 2019-05-26 22:18:16

我对此不是百分之百肯定,但我会试一试。

首先,让我们回顾一下这里使用的一些东西。

  • 逗号运算符丢弃前n-1个表达式结果并返回第n个结果。它经常被用作sequence point,因为它保证表达式将被按顺序求值。
  • 这里使用__extension__,它是一个GNU LibC宏,用于在通过-ansi-pedantic等指定冗长警告的编译环境下屏蔽关于头部中的GNU特定扩展的任何警告。通常在这样的编译器下,使用编译器特定的扩展会抛出警告(如果你在-Werror下运行,这是很常见的),但由于在使用GNU库和编译器的情况下,libc允许自己在可以安全使用的地方使用一些扩展。

现在,由于实际的断言逻辑可能使用GNU扩展(如使用__extension__所示),因此将屏蔽表达式本身在给定语义(即传递给assert(expr)的表达式)的情况下可能引发的任何实际警告,因为该表达式将在语义上位于__extension__块中并因此被屏蔽。

因此,需要有一种方法让编译器有机会显示这些警告,但不计算实际的表达式(因为表达式可能会有副作用,双重计算可能会导致不希望的行为)。

可以通过使用sizeof运算符来实现这一点,该运算符接受一个表达式,查看其类型,并找到它所占用的字符数--而无需实际计算表达式。

例如,如果我们有一个函数int blow_up_the_world(),则表达式sizeof(blow_up_the_world())将查找表达式结果的大小(在本例中为int),而不实际计算表达式。在这种情况下使用sizeof()实际上意味着世界不会被炸毁。

但是,如果传递给assert(expr)expr包含否则会触发编译器警告的代码(例如,在-pedantic-ansi模式下使用扩展),则编译器仍然会显示这些警告,即使代码在sizeof()中-警告本来会被屏蔽在__extension__块中。

接下来,我们看到它们不是直接将expr传递给sizeof,而是使用三元。这是因为三元组的类型是两个结果表达式都具有的类型-在本例中是int或类似的类型。这是因为将某些内容传递给sizeof会导致运行时值-即在variable length arrays的情况下-这可能会产生不希望看到的效果,或者可能会产生错误,例如当passing sizeof a function name时。

最后,他们想要所有这些,但在实际计算之前,并希望将assert()保留为表达式,因此,他们不使用do{}while()块或类似的东西,而是使用逗号运算符来丢弃第一个sizeof()技巧的结果。

票数 4
EN

Stack Overflow用户

发布于 2019-05-26 22:14:52

  1. ({不是标准C语言,它会在标准C编译模式下触发警告或错误。因此,他们使用的是__extension__,这将禁用任何此类diagnostics.
  2. However __extension__还会屏蔽expr中的非标准构造,这就是他们需要expr重复两次的原因,一次在__extension__中,一次在expr中。

<__extension__>H118expr>因此,他们通过在D23中放置另一个<D19来抑制另一次求值。但是,因为它不适用于像函数名这样的东西。,,

  1. ,所以用sizeof((expr) ? 1 : 0)代替,它没有这个问题。因此,生成的表达式的两部分是(a) assertion.
  2. Finally,和(b) __extension__(...)部分。
  3. 如果expr.
  4. The出现问题,则只需要第一部分来生成诊断。第二部分执行实际的assertion.
  5. Finally,,这两部分与逗号运算符连接。
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/56314110

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档