!!“在C代码?
我在/usr/include/linux/kernel.h中遇到了这个奇怪的宏代码:
/* Force a compilation error if condition is true, but also produce a
result (of value 0 and type size_t), so the expression can be used
e.g. in a structure initializer (or where-ever else comma expressions
aren't permitted). */
#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
#define BUILD_BUG_ON_NULL(e) ((void *)sizeof(struct { int:-!!(e); }))
什么:-!!
做?
实际上,这是一种检查表达式e是否可以评估为0的方法,如果不是,则使构建失败 。
这个宏有点错误; 它应该更像BUILD_BUG_OR_ZERO
,而不是...ON_ZERO
。 ( 偶尔有人讨论这是否是一个令人困惑的名字 。)
你应该读这样的表达式:
sizeof(struct { int: -!!(e); }))
(e)
:计算表达式e
。
!!(e)
:逻辑取反两次: 0
如果e == 0
; 否则1
。
-!!(e)
:在数字上否定步骤2中的表达式: 0
如果它为0
; 否则为-1
。
struct{int: -!!(0);} --> struct{int: 0;}
:如果它是零,那么我们声明一个带有宽度为零的匿名整数位域的结构。 一切都很好,我们照常进行。
struct{int: -!!(1);} --> struct{int: -1;}
:另一方面,如果它不是零,那么它会是一些负数。 声明负宽度的任何位域是一个编译错误。
所以我们要么在结构体中有一个宽度为0的位域,这很好,或者是一个负宽度的位域,这是一个编译错误。 然后我们取这个字段的sizeof
,所以我们得到一个具有适当宽度的size_t
(在e
为零的情况下它将为零)。
有人问: 为什么不使用assert
?
keithmo在这里的答案有很好的回应:
这些宏实现了一个编译时测试,而assert()是一个运行时测试。
非常正确。 您不希望在运行时检测到您的内核中可能早期发现的问题! 这是操作系统的关键部分。 无论在编译时可以检测到什么程度的问题,都会更好。
该:
是一个位域。 至于!!
,这是逻辑双重否定,因此返回0
代表假或1
代表真。 而-
是一个负号,即算术否定。
这只是一个窍门,让编译器在无效输入上陷入困境。
考虑BUILD_BUG_ON_ZERO
。 当-!!(e)
评估为负值时,会产生编译错误。 否则-!!(e)
评估为0,0宽度位域的大小为0.因此,宏评估的值为0的size_t
。
在我看来这个名字很弱,因为当输入不为零时,构建实际上会失败。
BUILD_BUG_ON_NULL
非常相似,但产生一个指针而不是int
。
有些人似乎将这些宏与assert()
混淆。
这些宏实现一个编译时测试,而assert()
是一个运行时测试。
上一篇: !!" in C code?