What does ":-!!" mean in C language?

Asked

Viewed 739 times

24

In a Linux kernel library, specifically this: /usr/include/linux/kernel. h, there’s a macro with a code strange to me:

/* 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 ":-!!" mean in C language?

  • 3
    • negative sign, ! logical negation. See this response in OS.com http://stackoverflow.com/a/9229793/2556111

3 answers

22


The !! is an old trick to convert numerical values into booleans.

  • The first ! denies the value if it is 0 flipped 1, if it’s anything else 0.
  • The second ! deny it again

The next part is to use this in defining a bit field in a struct.

When defining an integer numeric field in a struct you can tell how many bits it will occupy:

struct Fields {
    int a: 6; //Ocupa 6 bits
    int b: 2; //Ocupa 2 bits
}; 

And the number of bits cannot be negative. Hence the - before the !!: If e is non-zero we would have a bit field with negative size, which gives compilation error. If e for zero we will have a bit field of zero size, which is valid, although not much use. But since all that is a set of macros to check constants in compile time turns out to be useful.

8

!! is double negation. It is a trick used in some languages to convert a result to Boolean. The first ! (from right to left) denies the expression (e) with an implicit cast for Boolean, and the second ! deny again.

The - is a simple mathematical subtraction operator.

Then, if e is false, an integer 0 wide is defined struct{int: 0;}. If e is true, an integer with negative width is defined struct{int: -1;}, causing a build error.

7

In a bitfield structure you can specify the bit size of each member, keeping its original features. Example:

struct bitfield {
    signed int a : 3; // Um inteiro usando complemento de dois com 3 bits. Entre -4 e 3.
    unsigned b : 4;   // Um inteiro sem sinal. Valores entre 0 e 15.
};

And as there is no padding among the members1 (only at the end) is commonly used in interfaces with hardware or to read some types of files.

The detail is that the expression denoting the size of each member is not an integral literal, but a constant expression that results in an integral2. So the following is valid:

struct bitfield {
    int a : 1+1;
};

The rule also states that if the size is zero the member will have no effect (effectively it will not exist) and that this size cannot be negative. I can then create a macro like this:

#define ERROR_IF_NEGATIVE(e) struct { int : e; };

Note the missing struct and member name. But of course, this macro will fail with very large values (which exceed the bit size in an int). We can improve a little using double denial. !!e is 0 if e for 0 and 1 if e for any other value. So we can do -!!e to obtain a negative value if the condition is true.

#define ERROR_IF(e) struct { int : -!!e; };

But note that this macro can be used only where defining a structure is valid, but not in the middle of other expressions. This is where the sizeof. In C the sizeof of an empty structure is always 03:

#define ZERO_UNLESS(e) (sizeof(struct { int : -!!e; }));

Now you can use in the middle of any expression. If the condition is true you will get a build error. Otherwise the result will be zero.

int a = 4 + ZERO_UNLESS(sizeof(int) > 2);

This type of macro is similar to assert, but the condition must be a constant expression and will be evaluated at compile time, generating no performance cost. The operation is equivalent to static_assert of C++11.


  1. A lot of exceptions. Most should be defined by the implementation. Session 6.7.2.1/11:

    An implementation may allocate any addressable Storage Unit large enough to hold a bitfield. If enough space remains, a bit-field that immediately Follows Another bit-field in a Structure Shall be packed into Adjacent bits of the same Unit. If insufficient space remains, whether a bit-field that does not fit is put into the next Unit or overlaps Adjacent Units is implementation-defined. The order of allocation of bit-Fields Within a Unit (high-order to low-order or low-order to high-order) is implementation-defined. The Alignment of the addressable Storage Unit is unspecified.

  2. Session 6.7.2.1/4:

    The Expression that specifies the width of a bit-field Shall be an integer Constant Expression with a nonnegative value that does not exceed the width of an Object of the type that would be specified Were the Colon and Expression omitted. If the value is zero, the declaration Shall have no declarator.

  3. I couldn’t find a reference to prove it. It seems to be an extension of GCC and not a behavior defined by C, I’m not sure. Already in C++ o sizeof of an empty structure is 1.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.