原文: http://stackoverflow.com/questions/9229601/what-is-in-c-code
===============================================================================================================================
I bumped into this strange macro code in /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?
===============================================================================================================================
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.
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.)
You should read the expression like this:
sizeof(struct { int: -!!(e); }))
-
(e)
: Declare an expressione
. -
!!(e)
: Negate it twice. This produces 0 ife
was 0 originally, or a nonzero positive number if it wasn't. -
-!!(e)
: Multiply the value by -1. This results in 0 if step 2 was 0, or a negative number if it wasn't. -
struct{int: -!!(0);} --> struct{int: 0;}
: If it was zero, then we declare a struct with an integer bitfield that has width zero. Everything is fine and we proceed as normal. -
struct{int: -!!(1);} --> struct{int: -1;}
: On the other hand, if it isn't zero, then it will be some negative number. Declaring a bitfield with negative width is a compilation error.
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 sizeof
that field, so we get a size_t
with the appropriate width (which will be zero in the case where e
is zero).
Some people have asked: Why not just use an assert
?
keithmo's answer here has a good response:
These macros implement a compile-time test, while assert() is a run-time test.
Exactly right. You don't want to detect problems in your kernel at 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.
===============================================================================================================================
The :
is a bitfield. As for !!
, that is logical double negation and so returns 0
for false or 1
for true. And the-
is a minus sign, i.e. arithmetic negation.
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 asize_t
with value 0.
The name is weak in my view because the build in fact fails when the input is not zero.
BUILD_BUG_ON_NULL
is very similar, but intended for use with a pointer input.
===============================================================================================================================
Some people seem to be confusing these macros with assert()
.
These macros implement a compile-time test, while assert()
is a run-time test.
===============================================================================================================================
It's creating a size 0
bitfield 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 -1
bitfield is created, of course).
===============================================================================================================================