Checking sizeof at compile time

Sometimes you may have some C code which contains a struct which has some constraints on its size. For example, maybe the struct has to be aligned on certain byte boundaries due to hardware constraints (maybe it’s a DMA buffer to some device which uses lower bits of the address for its own purposes), and you need an array of such structs, and for each member of the array to be properly aligned, the size of the struct must be a multiple of, say, 8.

You might be tempted to try something like:

        #if ((sizeof(struct mystruct) % 8 ) != 0)
        #error "you screwed up struct mystruct again!"
        #endif

which won’t work for a number of reasons, one of which is probably that I’ve botched the preprocessor syntax, but most importantly it won’t work because the preprocessor has no idea about C types, and thus no idea what sizeof is.

So, you may have settled for some runtime code to do:

        if ((sizeof(struct mystruct) % 8 ) != 0) {
                printf("You screwed up mystruct again!\n");
                exit(1);
        }

But this is less than satisfactory, because you don’t catch the problem until runtime.

Is there a way to catch this kind of thing at compile time? Yes there is. I came across this in the linux kernel, in include/linux/kernel.h:

#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))

With that bit of black magic, you can then put this in your code:

{
        /* check that you didn't screw up mystruct */
        BUILD_BUG_ON(sizeof(struct mystruct) % 8));
}

This will fail to compile if sizeof(struct mystruct) is not a multiple of 8. If struct mystruct is a multiple of 8, it will insert no runtime code at all.

Cool.

But, wtf is it doing? How does it work?

It relies on a few tricks. It may help to think about what any such macro must do. We want a macro that evaluates an expression. If the expression is true, the macro must compile without warnings, and produce no code at all. When the expression is false, it must not compile.

So we need some kind of statement which doesn’t compile depending on the value of a number. Some genius had the brilliant idea that an array with a negative size won’t compile, but will compile with a size of 1 or greater (even a zero sized array is legal nowadays.)

        char myarray[1];   /* this will compile. */
        char myarray2[-1]; /* this will not compile */

But, we don’t actually want to declare an array, so it is put in terms of a type being measured by sizeof.

        sizeof(char[1]); /* this gives you the size of a */
                         /* character array of 1. */
        sizeof(char[-11]); /* this doesn't compile */

And how to get this into a form which generates zero code when it compiles? Well, it so happens that a bare expression is a valid statement in C. So this:

        1; /* this is a legal C statement with no effect. */

is a legal C statement with no effect. Well, almost no effect. It has the effect that gcc -Wall will complain:

        warning: statement with no effect

That’s what the “(void)” is for.

        (void) 1;

is a valid C statement with no effect, not even a warning from gcc -Wall.

So, what about the ‘!!’ in the expression “sizeof(char[1 – 2*!!(condition)]”? Near as I can tell, that is converting all non-zero values into the value 1.

So, if the argument to BUILD_BUG_ON is non zero, the macro evaluates to:

        (void) sizeof(char[-1]);

which fails to compile, which is what we want. and if the value of the argument to BUILD_BUG_ON is zero, then the macro evaluates to:

        (void) sizeof(char[1]);

which compiles with no warnings, and produces no code.

Pretty slick.

~ by scaryreasoner on February 28, 2009.

5 Responses to “Checking sizeof at compile time”

  1. […] code was in a kernel driver, and I knew the size of the structure was incorrect (as there was some some magic macros watching my back. I knew that the consequence of this being wrong was that the kernel would panic […]

  2. […] actually did have a BUILD_BUG_ON in the code that is designed to catch precisely this problem at build time, but somehow in a patch, […]

  3. This is ingenious! This is what I need!

  4. !! is not a special operator; it’s just an application of the logical not operator ! twice. A single ! yields to one if the value is zero and zero otherwise, a second ! negates the result thus yielding to zero if the original value was zero and to one otherwise; which is what it is all about.

  5. It doesn’t work to me, no error is raised while compiling (gcc and g++).

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

Join 39 other followers

%d bloggers like this: