1805

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?


Update: In recent times, the macro has been moved to /usr/include/linux/build_bug.h

Jan Schultke
  • 17,446
  • 6
  • 47
  • 96
chmurli
  • 15,080
  • 3
  • 16
  • 12
  • 83
    git blame tells us that this particular form of static assertion was [introduced by Jan Beulich in 8c87df4](https://github.com/mirrors/linux-2.6/commit/8c87df457cb58fe75b9b893007917cf8095660a0). Obviously he had good reasons to do it (see the commit message). – Niklas B. Feb 10 '12 at 16:34
  • 2
    - Unary minus
    ! Logical NOT
    inverse not not of the given Integer e so the variable can either be 0 or 1.
    – CyrillC Feb 10 '12 at 14:58
  • Wait, I thought the arguments of `sizeof` are not evaluated. Is that wrong in this case? If so, why? Because it's a macro? – Phillip Cloud Mar 11 '12 at 15:49
  • 3
    @cpcloud, `sizeof` does "evaluate" the type, just not the value. Its the type thats invalid in this case. – Winston Ewert Mar 24 '12 at 17:19
  • 2
    Almost goes without saying that the bitfield created is an anonymous one. This is in the same spirit as C++ template meta-programming, i.e. have things happen at compile time that can be checked at compile time. – phorgan1 Feb 16 '12 at 03:05
  • Several of the answers mention that `:0` gives a zero-sized anonymous bit-field, and therefore a zero-sized struct. That's not quite true; in standard C, `:0` is not any kind of field declaration (you can't give it a name) but rather a directive to start the _next_ bit-field on the next word boundary (typically `int` but not necessarily). It doesn't normally have any significance unless used between two (otherwise adjacent) bit-field declarations. The resulting struct is zero-sized because it contains _no_ declarations; and that of course is a gcc extension. – Martin Kealey Aug 18 '22 at 05:24

5 Answers5

1805

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); }))
  1. (e): Compute expression e.

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

  3. -!!(e): Numerically negate the expression from step 2: 0 if 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't zero, then it will be some negative number. Declaring any 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.

Community
  • 1
  • 1
John Feminella
  • 303,634
  • 46
  • 339
  • 357
  • 2
    So how does this macro get used, i.e. what value or expression for `e` is used? – weston Feb 10 '12 at 16:03
  • 5
    @weston Lots of different places. **[See for yourself!](http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux.git&a=search&h=HEAD&st=grep&s=BUILD_BUG_ON_ZERO)** – John Feminella Feb 10 '12 at 16:06
  • 196
    recent variants of C++ or C standards have something like `static_assert` for related purposes. – Basile Starynkevitch Feb 10 '12 at 17:00
  • @Basile: With recent variants of C++ you should use template metaprogramming :) – vsz Feb 10 '12 at 17:16
  • 60
    @Lundin - #error would require use of 3 lines of code #if/#error/#endif, and would only work for evaluations accessible to the pre-processor. This hack works for any evaluation accessible to the compiler. – Ed Staub Feb 10 '12 at 17:50
  • 261
    The Linux kernel does not use C++, at least not while Linus is still alive. – Mark Ransom Feb 10 '12 at 17:52
  • 4
    this is why static asserts have been introduced in D – ratchet freak Feb 10 '12 at 20:33
  • 3
    re: static_assert, this is not available everywhere Linux builds. – Hejazzman Feb 11 '12 at 02:53
  • 1
    "You don't want to detect *any* problems at runtime that could have been caught earlier!" Fixed that for you. – tenfour Mar 20 '12 at 22:08
  • 3
    The another important point of this test is whether the expression can be evaluated at the compile time, the build would fail if it couldn't be. – konrad.kruczynski May 19 '12 at 12:07
  • 10
    @Dolda2000: "*Boolean expressions in C are defined to always evaluate to zero or one*" -- Not exactly. The *operators* that yield "logically boolean" results (`!`, `<`, `>`, `<=`, `>=`, `==`, `!=`, `&&`, `||`) always yield 0 or 1. Other expressions may yield results that may be used as a conditions, but are merely zero or non-zero; for example, `isdigit(c)`, where `c` is a digit, can yield *any* non-zero value (which is then treated as true in a condition). – Keith Thompson Apr 03 '13 at 03:23
  • 5
    Since when is `sizeof` any type allowed to be zero? That violates the uniqueness of addresses of array elements. (And this is C, so let's not get into empty base class optimization, which still doesn't result in any object having zero size) – Ben Voigt Jul 11 '13 at 05:48
  • 3
    @BenVoigt The C standard does not allow zero-size types. The Linux kernel relies on numerous `gcc` compiler extensions that aren't part of the standard. See, e.g., here: http://stackoverflow.com/questions/8143417/why-is-linux-kernel-coded-using-non-standard-c-gcc-specific-features – John Feminella Jul 11 '13 at 17:11
  • 2
    @John: I understand that the kernel uses gcc extensions. But extensions merely give meaning to invalid code. If a compiler changes the meaning of valid code, that's a bug. – Ben Voigt Jul 11 '13 at 17:18
  • 3
    I've been doing C programming for a while, and haven't seen this "bit field" functionality. So for anyone in same position, here's a good tutorial: http://www.tutorialspoint.com/cprogramming/c_bit_fields.htm – Alex Apr 08 '14 at 15:11
  • 9
    Quick note about the name. It's called `...ON_ZERO` because it's a derivative of `BUG_ON`, a macro that's essentially an assertion. `BUG_ON(foo)` means "it's a bug if `foo` is true" (at runtime). Conversely, `BUILD_BUG_ON` is a static assertion (checked at build time), and finally `BUILD_BUG_ON_ZERO` is exactly the same, except that the whole thing is an expression equal to `(size_t)0`, as the comment in the question states. – Xion Nov 21 '16 at 23:37
  • 3
    @Xion: thanks for that explanation. But I'll confess that I find the name `BUILD_BUG_ON_ZERO` extremely confusing and wish I didn't need to search the internet to understand how it's used. I'm not sure what name would work for everyone, but as far as I'm concerned I wish the name `BUILD_BUG_OR_ZERO` had been used. – Michael Burr Jan 27 '17 at 01:35
  • Anyway we can't use these macro for a variable. right? `error: bit-field ‘’ width not an integer constant` Its allowing only constants. So, what's the use? – Karthik Raj Palanichamy Mar 24 '17 at 10:30
  • why to do double negation, because end result of double negation is same as original value. – Dinesh Aug 24 '17 at 17:30
  • @Dinesh You are confused with `~`. `!` only yields 0 or 1. – Mr Lister Mar 05 '18 at 14:35
  • 1
    I think the answer should mention that `struct { int :0}` is `undefined behaviour` according to the C standard. https://stackoverflow.com/questions/4297095/practical-use-of-zero-length-bitfields#12918937 – Patrick Schlüter Jun 13 '18 at 12:52
  • There is an additional closing parenthesis in the last which belonged to the macro, in case someone is wondering that why an extra closing parenthesis. PS - I tried to edit, but edit should be at least 6 characters long. – Divyanshu Dec 10 '18 at 02:15
  • @Hejazzman re: static_assert, You could use static_assert like this, right? `#if (__STDC_VERSION__ >= 201100L) #include #else #define static_assert(e, txt) BUILD_BUG_ON_ZERO(e) #endif` – alx - recommends codidact Feb 18 '19 at 18:11
273

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 a size_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 yields a pointer rather than an int.

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • 17
    is `sizeof(struct { int:0; })` strictly conforming? – ouah Feb 10 '12 at 15:08
  • 8
    Why would the result in general be `0`? A `struct` with only an empty bitfield, true, but I don't think that struct with size 0 are allowed. E.g if you'd create an array of that type, the individual array elements still must have different addresses, no? – Jens Gustedt Feb 10 '12 at 15:09
  • 2
    they actually don't care as as they use GNU extensions, they disable strict aliasing rule and don't consider integer overflows as UB. But I was wondering if this is strictly conforming C. – ouah Feb 10 '12 at 15:13
  • 3
    @ouah regarding unnamed zero length bitfields, see here: http://stackoverflow.com/questions/4297095/practical-use-of-zero-length-bitfields#4297110 – David Heffernan Feb 10 '12 at 15:14
  • 2
    @DavidHeffernan interesting, but the quote is from C++ standard not C – ouah Feb 10 '12 at 15:26
  • size 0 bit fields are used for forcing alignment. – EvilTeach Feb 10 '12 at 15:33
  • 2
    @ouah Pretty much identical text is present in the C standard also. – David Heffernan Feb 10 '12 at 15:36
  • 11
    @DavidHeffernan actually C allows unamed bit-field of `0` width, but not if there no other named member in the structure. `(C99, 6.7.2.1p2) "If the struct-declaration-list contains no named members, the behavior is undefined."` So for example `sizeof (struct {int a:1; int:0;})` is strictly conforming but `sizeof(struct { int:0; })` is not (undefined behavior). – ouah Oct 16 '12 at 13:51
  • @DavidHeffernan: can you, please, detaliate, why the result of !!(e) is either 0 or 1 (and no other non-zero number)? I miss that part. What happens to the other bits in original e that are negated twice? They do not retrieve their original values? Thank you. – user1284631 Nov 29 '12 at 11:45
  • 1
    @axeoth `!` is *logical* negation and '~' is *bitwise* negation. So we are dealing here with logical negation. The logical negation operator evaluates to either 0 or 1. – David Heffernan Nov 29 '12 at 11:54
  • @JensGustedt: It would not be practical to uphold for objects of zero size the same guarantees that would apply to objects of non-zero size, but the same is true of real zero as well. There should have been no problem saying that if p and q are pointers to zero-size type, adding or subtracting any value from p will yield p, and p-q will yield 0 if p==q and an Unspecified Value in all other cases. The *only* downside I can see would be that it would render useless code which attempts to use arrays of size zero (rather than -1) to force compiler errors when required conditions don't hold. – supercat Apr 14 '17 at 21:47
176

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

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

Update:

As of C11, the _Static_assert() keyword is available to create compile time tests, and should be used unless code is being written for old compilers.

user16217248
  • 3,119
  • 19
  • 19
  • 37
keithmo
  • 4,893
  • 1
  • 21
  • 17
58

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

In sympathy for the need for compile-time assertions, GCC 4.3 introduced the error function attribute that 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!

#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_assert which uses this feature and most of the macros in bug.h have been updated accordingly. Still, this macro can't be used as an initializer. However, using by statement expressions (another GCC C-extension), you can!

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

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.

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.

HoldOffHunger
  • 18,769
  • 10
  • 104
  • 133
Daniel Santos
  • 3,098
  • 26
  • 25
  • 6
    All of your solutions are NOT alternatives. The comment above the macro is pretty clear "`so the expression can be used e.g. in a structure initializer (or where-ever else comma expressions aren't permitted).`" The macro returns an expression of type `size_t` – Wiz Jul 22 '13 at 18:09
  • 3
    @Wiz Yes, I am aware of this. Perhaps this was a bit verbose and maybe I need to re-visit my wording, but my point was to explore the various mechanisms for static assertions and show why we're still using negative sized bitfields. In short, if we get a mechanism for constant statement expression, we will have other options open. – Daniel Santos Jul 23 '13 at 20:52
  • Anyway we can't use these macro for a variable. right? `error: bit-field ‘’ width not an integer constant` Its allowing only constants. So, what's the use? – Karthik Raj Palanichamy Mar 24 '17 at 10:30
  • 1
    @Karthik Search the sources of the Linux kernel to see why it's used. – Daniel Santos Mar 25 '17 at 17:38
39

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

Niklas B.
  • 92,950
  • 18
  • 194
  • 224
Matt Phillips
  • 9,465
  • 8
  • 44
  • 75