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

24

In a Linux kernel library, specifically this: / usr / include / linux / kernel.h , there is a macro with a strange code for 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?

    
asked by anonymous 04.02.2014 / 12:04

3 answers

22

!! is an old trick to convert numeric values to booleans.

  • The first ! negates the value, if it is 0 it will 1 , if it is anything else it will 0 .
  • Second ! deny this 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 say 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 can not be negative. So - before !! : If e is non-zero we would have a bit field with negative size, which gives compilation error. If e is zero we will have a bit field of size zero, which is valid, although it does not have much use. But since it's all a set of macros to check for constants at compile time it turns out to be useful.

    
04.02.2014 / 12:19
8

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

The - is a simple mathematical operator of subtraction.

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

    
04.02.2014 / 12:16
6

In a bitfield structure you can specify the bit size of each member while retaining its original characteristics. 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 since there is no padding between members 1 (only at the end) is commonly used on hardware interfaces 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 rather a constant expression that results in an integral 2 . 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 (it will not actually exist) and that size can not be negative. I can then create a macro like this:

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

Notice the omission of the struct and member name. But of course, this macro will fail with very large values (that exceed the size in bits in an int). We can improve a bit by using double negation. !!e is 0 if e is 0 and 1 if e is any other value. So we can -!!e to get 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 sizeof enters. In C, the% w of an empty structure is always sizeof 3 :

#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 have a compilation error. Otherwise the result will be zero.

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

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

  • A lot of exceptions. Most must be defined by implementation. Session 6.7.2.1/11:

      

    An implementation may allocate any addressable storage unit large enough to hold a bitfield. If sufficient space remains, the 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, if a bit-field that does not fit 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.

  • Session 6.7.2.1/4:

      The expression that specifies the width of the 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 and the colon and expression omitted. If the value is zero, the declaration shall have no declarator.

  • I could not find a reference to prove this. It seems to be an extension of GCC and not a C-defined behavior, I'm not sure. In C ++ the static_assert of an empty structure is sizeof .

  • 04.02.2014 / 15:21