什么是?:-&引用;用C代码?
我在中遇到了这个奇怪的宏代码:什么是?:-&引用;用C代码?,c,linux,macros,linux-kernel,C,Linux,Macros,Linux Kernel,我在中遇到了这个奇怪的宏代码: 什么是:-执行?该:是一个位字段。至于,即返回0表示false,返回1表示true。而-是负号,即算术求反 这只是让编译器吐出无效输入的一个技巧 考虑BUILD\u BUG\u ON\u ZERO。当-!!(e) 的计算结果为负值,从而产生编译错误。否则-!!(e) 计算结果为0,0宽度的位字段大小为0。因此,宏的计算结果为值为0的size\u t 在我看来,这个名称很弱,因为当输入不是零时,构建实际上失败了 BUILD\u BUG\u ON\u NULL非常相似
什么是
:-代码>执行?该:
是一个位字段。至于
,即返回0
表示false,返回1
表示true。而-
是负号,即算术求反
这只是让编译器吐出无效输入的一个技巧
考虑BUILD\u BUG\u ON\u ZERO
。当-!!(e)
的计算结果为负值,从而产生编译错误。否则-!!(e)
计算结果为0,0宽度的位字段大小为0。因此,宏的计算结果为值为0的size\u t
在我看来,这个名称很弱,因为当输入不是零时,构建实际上失败了
BUILD\u BUG\u ON\u NULL
非常相似,但生成的是指针而不是int如果条件为false,它将创建一个size0
位字段,如果条件为true/非零,则创建一个size-1
(-!!1
)位字段。在前一种情况下,没有错误,并且使用int成员初始化结构。在后一种情况下,会出现编译错误(当然,不会创建大小-1
位字段)。这实际上是一种检查表达式e是否可以计算为0的方法,如果不能,则检查生成是否失败
宏有点命名错误;它应该更像是BUILD\u BUG\u或\u ZERO
,而不是…ON\u 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\u t
(在e
为零的情况下,该值为零)
有人问:为什么不直接使用断言?
?
这里有一个很好的回应:
这些宏实现编译时测试,而assert()是运行时测试
完全正确。您不希望在运行时检测到内核中的问题,这些问题可能在早些时候就被发现了!它是操作系统的关键部分。无论编译时可以检测到多大程度的问题,那就更好了。有些人似乎把这些宏与assert()
混淆了
这些宏实现编译时测试,而assert()
是运行时测试。好吧,我很惊讶没有提到这种语法的替代方法。另一种常见(但较旧)机制是调用未定义的函数,如果断言正确,则依赖优化器编译出函数调用
#define MY_COMPILETIME_ASSERT(test) \
do { \
extern void you_did_something_bad(void); \
if (!(test)) \
you_did_something_bad(void); \
} while (0)
虽然这种机制可以工作(只要启用了优化),但它的缺点是在链接之前不会报告错误,此时它无法找到您所做的函数的定义。这就是为什么内核开发人员开始使用一些技巧,比如负大小的位字段宽度和负大小的数组(后者在GCC4.4中停止破坏构建)
为了支持编译时断言的需要,GCC4.3引入了,它允许您扩展这一旧概念,但会生成一个编译时错误,其中包含您选择的消息——不再有神秘的“负大小数组”错误消息
#define MAKE_SURE_THIS_IS_FIVE(number) \
do { \
extern void this_isnt_five(void) __attribute__((error( \
"I asked for five and you gave me " #number))); \
if ((number) != 5) \
this_isnt_five(); \
} while (0)
事实上,从Linux3.9开始,我们现在有一个名为的宏,它使用了此功能,并且中的大多数宏都已相应地更新。但是,该宏不能用作初始值设定项。但是,使用by(另一个GCC C扩展),您可以
#define ANY_NUMBER_BUT_FIVE(number) \
({ \
typeof(number) n = (number); \
extern void this_number_is_five(void) __attribute__(( \
error("I told you not to give me a five!"))); \
if (n == 5) \
this_number_is_five(); \
n; \
})
如果表达式的计算结果为5或不是编译时常量,此宏将精确计算其参数一次(以防有副作用),并创建一个编译时错误,该错误会显示“我告诉过你不要给我5!”
那么,我们为什么不使用它来代替负大小的位字段呢?遗憾的是,目前对语句表达式的使用有很多限制,包括它们作为常量初始值设定项的使用(对于枚举常量、位字段宽度等),即使语句表达式本身是完全恒定的(即,可以在编译时完全计算,否则通过测试)。此外,它们不能在功能体之外使用
希望GCC能很快修正这些缺点,并允许常量语句表达式用作常量初始值设定项。这里的挑战是定义什么是法律常量表达式的语言规范。C++11仅为这种类型或事物添加了constexpr关键字,但C11中不存在对应项。虽然C11确实得到了静态断言,这将解决部分问题,但它无法解决所有这些缺点。因此,我希望gcc能够通过-std=gnuc99&-std=gnuc11或类似的方式将constexpr功能作为扩展提供,并允许在语句表达式等上使用它。实际上,如果条件为真,它将返回值为0的size\u t
。issizeof(struct{int:0;})
#define ANY_NUMBER_BUT_FIVE(number) \
({ \
typeof(number) n = (number); \
extern void this_number_is_five(void) __attribute__(( \
error("I told you not to give me a five!"))); \
if (n == 5) \
this_number_is_five(); \
n; \
})