Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/23.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
什么是?:-&引用;用C代码?_C_Linux_Macros_Linux Kernel - Fatal编程技术网

什么是?:-&引用;用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,它将创建一个size
0
位字段,如果条件为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
    。is
    sizeof(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;                                                    \
        })