Linux 什么是 ”:-!!” 在 C 代码中?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9229601/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 04:30:56  来源:igfitidea点击:

What is ":-!!" in C code?

clinuxmacroslinux-kernel

提问by chmurli

I bumped into this strange macro code in /usr/include/linux/kernel.h:

我在/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); }))

What does :-!!do?

有什么作用:-!!

采纳答案by John Feminella

This is, in effect, a way to check whether the expression e can be evaluated to be 0, and if not, to fail the build.

这实际上是一种检查表达式 e 是否可以被评估为 0 的方法,如果不是,则构建失败

The macro is somewhat misnamed; it should be something more like BUILD_BUG_OR_ZERO, rather than ...ON_ZERO. (There have been occasional discussions about whether this is a confusing name.)

这个宏有点名不副实;它应该更像BUILD_BUG_OR_ZERO,而不是...ON_ZERO. (偶尔会讨论这是否是一个令人困惑的名字。)

You should read the expression like this:

你应该像这样阅读表达式:

sizeof(struct { int: -!!(e); }))
  1. (e): Compute expression e.

  2. !!(e): Logically negate twice: 0if e == 0; otherwise 1.

  3. -!!(e): Numerically negate the expression from step 2: 0if it was 0; otherwise -1.

  4. struct{int: -!!(0);} --> struct{int: 0;}: If it was zero, then we declare a struct with an anonymous integer bitfield that has width zero. Everything is fine and we proceed as normal.

  5. struct{int: -!!(1);} --> struct{int: -1;}: On the other hand, if it isn'tzero, then it will be some negative number. Declaring any bitfield with negativewidth is a compilation error.

  1. (e):计算表达式e

  2. !!(e): 逻辑否定两次: 0if e == 0; 否则1

  3. -!!(e): 对第 2 步中的表达式进行数值求反:0如果是0; 否则-1

  4. struct{int: -!!(0);} --> struct{int: 0;}:如果它为零,那么我们声明一个结构体,它带有一个宽度为零的匿名整数位域。一切都很好,我们照常进行。

  5. struct{int: -!!(1);} --> struct{int: -1;}: 另一方面,如果它不是零,那么它将是一些负数。声明任何具有宽度的位域都是编译错误。

So we'll either wind up with a bitfield that has width 0 in a struct, which is fine, or a bitfield with negative width, which is a compilation error. Then we take sizeofthat field, so we get a size_twith the appropriate width (which will be zero in the case where eis zero).

因此,我们要么在结构中使用宽度为 0 的位域,这很好,要么使用负宽度的位域,这是编译错误。然后我们取sizeof那个字段,所以我们得到一个size_t具有适当宽度的 a (在零的情况下e为零)。



Some people have asked: Why not just use an assert?

有人问:为什么不直接使用assert?

keithmo's answerhere has a good response:

keithmo 的回答在这里得到了很好的回应:

These macros implement a compile-time test, while assert() is a run-time test.

这些宏实现了编译时测试,而 assert() 是运行时测试。

Exactly right. You don't want to detect problems in your kernelat runtime that could have been caught earlier! It's a critical piece of the operating system. To whatever extent problems can be detected at compile time, so much the better.

非常正确。您不想在运行时检测内核中可能更早发现的问题!它是操作系统的关键部分。无论在何种程度上可以在编译时检测到问题,那就更好了。

回答by David Heffernan

The :is a bitfield. As for !!, that is logical double negationand so returns 0for false or 1for true. And the -is a minus sign, i.e. arithmetic negation.

:是位域。至于!!,这是逻辑双重否定,因此返回0false 或1true。并且-是一个减号,即算术否定。

It's all just a trick to get the compiler to barf on invalid inputs.

让编译器拒绝无效输入只是一个技巧。

Consider BUILD_BUG_ON_ZERO. When -!!(e)evaluates to a negative value, that produces a compile error. Otherwise -!!(e)evaluates to 0, and a 0 width bitfield has size of 0. And hence the macro evaluates to a size_twith value 0.

考虑BUILD_BUG_ON_ZERO。当-!!(e)评估为负值时,会产生编译错误。否则-!!(e)计算为 0,宽度为 0 的位域的大小为 0。因此宏计算为size_t值为 0 的 a。

The name is weak in my view because the build in fact fails when the input is notzero.

在我看来,这个名字很弱,因为当输入为零时,构建实际上会失败。

BUILD_BUG_ON_NULLis very similar, but yields a pointer rather than an int.

BUILD_BUG_ON_NULL非常相似,但产生一个指针而不是一个int

回答by Matt Phillips

It's creating a size 0bitfield if the condition is false, but a size -1(-!!1) bitfield if the condition is true/non-zero. In the former case, there is no error and the struct is initialized with an int member. In the latter case, there is a compile error (and no such thing as a size -1bitfield is created, of course).

0如果条件为假,则创建大小位域,但如果条件为真/非零,则创建大小-1( -!!1) 位域。在前一种情况下,没有错误并且结构使用 int 成员进行初始化。在后一种情况下,会出现编译错误(当然,不会-1创建大小位域之类的东西)。

回答by keithmo

Some people seem to be confusing these macros with assert().

有些人似乎将这些宏与assert().

These macros implement a compile-time test, while assert()is a runtime test.

这些宏实现了编译时测试,同时assert()是运行时测试。

回答by Daniel Santos

Well, I am quite surprised that the alternatives to this syntax have not been mentioned. Another common (but older) mechanism is to call a function that isn't defined and rely on the optimizer to compile-out the function call if your assertion is correct.

好吧,我很惊讶没有提到这种语法的替代方案。另一种常见(但较旧)的机制是调用未定义的函数,如果断言正确,则依赖优化器来编译函数调用。

#define MY_COMPILETIME_ASSERT(test)              \
    do {                                         \
        extern void you_did_something_bad(void); \
        if (!(test))                             \
            you_did_something_bad(void);         \
    } while (0)

While this mechanism works (as long as optimizations are enabled) it has the downside of not reporting an error until you link, at which time it fails to find the definition for the function you_did_something_bad(). That's why kernel developers starting using tricks like the negative sized bit-field widths and the negative-sized arrays (the later of which stopped breaking builds in GCC 4.4).

尽管此机制有效(只要启用了优化),它的缺点是在链接之前不会报告错误,此时它无法找到函数 you_did_something_bad() 的定义。这就是内核开发人员开始使用诸如负大小位域宽度和负大小数组(后者停止破坏 GCC 4.4 中的构建)之类的技巧的原因。

In sympathy for the need for compile-time assertions, GCC 4.3 introduced the the errorfunction attributethat allows you to extend upon this older concept, but generate a compile-time error with a message of your choosing -- no more cryptic "negative sized array" error messages!

出于对编译时断言的需要,GCC 4.3 引入了errorfunction 属性,允许您扩展这个旧概念,但会生成带有您选择的消息的编译时错误——不再是神秘的“负大小数组” " 错误信息!

#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)

In fact, as of Linux 3.9, we now have a macro called compiletime_assertwhich uses this feature and most of the macros in bug.hhave been updated accordingly. Still, this macro can't be used as an initializer. However, using by statement expressions(another GCC C-extension), you can!

事实上,从 Linux 3.9 开始,我们现在有一个compiletime_assert使用此功能的宏,并且其中的大部分宏bug.h已相应更新。不过,这个宏不能用作初始化程序。但是,使用 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;                                                    \
    })

This macro will evaluate its parameter exactly once (in case it has side-effects) and create a compile-time error that says "I told you not to give me a five!" if the expression evaluates to five or is not a compile-time constant.

这个宏将只计算一次它的参数(以防它有副作用)并创建一个编译时错误,上面写着“我告诉过你不要给我一个 5!” 如果表达式的计算结果为 5 或不是编译时常量。

So why aren't we using this instead of negative-sized bit-fields? Alas, there are currently many restrictions of the use of statement expressions, including their use as constant initializers (for enum constants, bit-field width, etc.) even if the statement expression is completely constant its self (i.e., can be fully evaluated at compile-time and otherwise passes the __builtin_constant_p()test). Further, they cannot be used outside of a function body.

那么为什么我们不使用它而不是负大小的位域呢?唉,目前语句表达式的使用有很多限制,包括它们作为常量初始化器的使用(对于枚举常量、位域宽度等),即使语句表达式本身是完全恒定的(即,可以完全计算在编译时,否则通过__builtin_constant_p()测试)。此外,它们不能在函数体之外使用。

Hopefully, GCC will amend these shortcomings soon and allow constant statement expressions to be used as constant initializers. The challenge here is the language specification defining what is a legal constant expression. C++11 added the constexpr keyword for just this type or thing, but no counterpart exists in C11. While C11 did get static assertions, which will solve part of this problem, it wont solve all of these shortcomings. So I hope that gcc can make a constexpr functionality available as an extension via -std=gnuc99 & -std=gnuc11 or some such and allow its use on statement expressions et. al.

希望 GCC 能尽快修正这些缺点,并允许将常量语句表达式用作常量初始值设定项。这里的挑战是定义什么是合法常量表达式的语言规范。C++11 只为这种类型或事物添加了 constexpr 关键字,但在 C11 中不存在对应的关键字。虽然 C11 确实得到了静态断言,这将解决部分问题,但它不会解决所有这些缺点。所以我希望 gcc 可以通过 -std=gnuc99 & -std=gnuc11 或类似的扩展来提供 constexpr 功能,并允许它在语句表达式等上使用。阿尔。