127

Is there any way to use a sizeof in a preprocessor macro?

For example, there have been a ton of situations over the years in which I wanted to do something like:

#if sizeof(someThing) != PAGE_SIZE
#error Data structure doesn't match page size
#endif

The exact thing I'm checking here is completely made up - the point is, I often like to put in these types of (size or alignment) compile-time checks to guard against someone modifying a data-structure which could misalign or re-size things which would break them.

Needless to say - I don't appear to be able to use a sizeof in the manner described above.

Melebius
  • 6,183
  • 4
  • 39
  • 52
Brad
  • 11,262
  • 8
  • 55
  • 74

13 Answers13

94

There are several ways of doing this. Following snippets will produce no code if sizeof(someThing) equals PAGE_SIZE; otherwise they will produce a compile-time error.

1. C11 way

Starting with C11 you can use static_assert (requires #include <assert.h>).

Usage:

static_assert(sizeof(someThing) == PAGE_SIZE, "Data structure doesn't match page size");

2. Custom macro

If you just want to get a compile-time error when sizeof(something) is not what you expect, you can use following macro:

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

Usage:

BUILD_BUG_ON( sizeof(someThing) != PAGE_SIZE );

This article explains in details why it works.

3. MS-specific

On Microsoft C++ compiler you can use C_ASSERT macro (requires #include <windows.h>), which uses a trick similar to the one described in section 2.

Usage:

C_ASSERT(sizeof(someThing) == PAGE_SIZE);
nevermind
  • 2,300
  • 1
  • 20
  • 36
  • 3
    The macro does not work in GNU `gcc` (tested at version 4.8.4) (Linux). At the `((void)sizeof(...` it errors with `expected identifier or '(' before 'void'` and `expected ')' before 'sizeof'`. But in principle `size_t x = (sizeof(...` instead does work as intended. You have to "use" the result, somehow. To allow for this to be called *multiple times* either inside a function or at global scope, something like `extern char _BUILD_BUG_ON_ [ (sizeof(...) ];` can be used repeatedly (no side-effects, do not actually reference `_BUILD_BUG_ON_` anywhere). – JonBrave Jan 02 '17 at 12:23
  • Been using static asserts for a lot longer than 2011 has been a year. – Dan Sep 18 '19 at 21:01
  • 1
    @Engineer: Because the question was asked and answered back in 2010 and at that time it was accepted, while THIS answer was posted 2013. You can't "unaccept" an answer later on (as to my knowledge at least), so, the now better answer is not the accepted one. – Bodo Thiesen Oct 12 '19 at 07:07
  • @BodoThiesen. Old answers can be exchanged for new ones at any time. – Engineer Oct 13 '19 at 05:20
  • 1
    @Engineer look, insanity has stopped ;) – Bodo Thiesen Oct 17 '19 at 19:11
  • Regarding BUILD_BUG_ON: Why is condition negated twice like !!(condition), would (condition) not be enough? – grenix Jul 23 '20 at 10:33
  • !! see https://stackoverflow.com/questions/31494179/different-behaviour-of-c-macro-for-different-cases – grenix Jul 23 '20 at 19:02
78

Is there anyway to use a "sizeof" in a pre-processor macro?

No. The conditional directives take a restricted set of conditional expressions; sizeof is one of the things not allowed.

Preprocessing directives are evaluated before the source is parsed (at least conceptually), so there aren't any types or variables yet to get their size.

However, there are techniques to getting compile-time assertions in C (for example, see this page).

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • Great article - clever solution! Though you have to admin - they really pushed C syntax to it's limit to get this one to work! :-O – Brad Nov 02 '10 at 16:21
  • 1
    Turns out - as the article even says - I am building Linux kernel code right now - and there is a define already in the kernel - BUILD_BUG_ON - where the kernel uses it for things like: BUILD_BUG_ON(sizeof(char) != 8) – Brad Nov 02 '10 at 16:30
  • 2
    @Brad BUILD_BUG_ON and others generating surely-incorrect code that will fail to compile (and give some non-obvious error message in process). Not really #if statement, so you cannot e.g. exclude block of code based on this. – keltar Oct 14 '13 at 08:21
16

I know it's a late answer, but to add on to Mike's version, here's a version we use that doesn't allocate any memory. I didn't come up with the original size check, I found it on the internet years ago and unfortunately can't reference the author. The other two are just extensions of the same idea.

Because they are typedef's, nothing is allocated. With the __LINE__ in the name, it's always a different name so it can be copied and pasted where needed. This works in MS Visual Studio C compilers, and GCC Arm compilers. It does not work in CodeWarrior, CW complains about redefinition, not making use of the __LINE__ preprocessor construct.

//Check overall structure size
typedef char p__LINE__[ (sizeof(PARS) == 4184) ? 1 : -1];

//check 8 byte alignment for flash write or similar
typedef char p__LINE__[ ((sizeof(PARS) % 8) == 0) ? 1 : 1];

//check offset in structure to ensure a piece didn't move
typedef char p__LINE__[ (offsetof(PARS, SUB_PARS) == 912) ? 1 : -1];
Paul
  • 311
  • 4
  • 8
  • This actually works really welll for a standard C project... I like it! – Ashley Duncan Feb 20 '18 at 08:55
  • 2
    This one should be the correct answer because of the zero allocation. Even better into a define: `#define STATIC_ASSERT(condition) typedef char p__LINE__[ (condition) ? 1 : -1];` – Renaud Cerrato Jul 25 '18 at 07:41
  • p__LINE__ does not produce a unique name. It produces p__LINE__ as a variable. You would need a preproc macro and use __CONCAT from sys/cdefs.h . – Coroos Mar 02 '20 at 11:37
9

I know this thread is really old but...

My solution:

extern char __CHECK__[1/!(<<EXPRESSION THAT SHOULD COME TO ZERO>>)];

As long as that expression equates to zero, it compiles fine. Anything else and it blows up right there. Because the variable is extern'd it will take up no space, and as long as no-one references it (which they won't) it won't cause a link error.

Not as flexible as the assert macro, but I couldn't get that to compile in my version of GCC and this will... and I think it will compile just about anywhere.

Scott
  • 91
  • 1
  • 3
  • 7
    *Never* invent your own macros starting with two underscores. This path lies madness (aka *undefined behavior*). – Jens Aug 31 '15 at 09:03
  • There are a bunch of examples listed on this page http://www.pixelbeat.org/programming/gcc/static_assert.html – benathon Mar 29 '16 at 20:53
  • doesnt work when compiled with arm gcc compiler. gives the expected error " error: variably modified '__CHECK__' at file scope" – thunderbird May 24 '17 at 05:31
  • @Jens You are right but this is literally not a macro, it’s a variable declaration. Of course, it may interfere with macros. – Melebius Jan 19 '18 at 11:25
5

The existing answers just show how to achieve the effect of "compile-time assertions" based on the size of a type. That may meet the OP's needs in this particular case, but there are other cases where you really need a preprocessor conditional based on the size of a type. Here's how to do it:

Write yourself a little C program like:

/* you could call this sizeof_int.c if you like... */
#include <stdio.h>
/* 'int' is just an example, it could be any other type */
int main(void) { printf("%zd", sizeof(int); }

Compile that. Write a script in your favorite scripting language, which runs the above C program and captures its output. Use that output to generate a C header file. For example, if you were using Ruby, it might look like:

sizeof_int = `./sizeof_int`
File.open('include/sizes.h','w') { |f| f.write(<<HEADER) }
/* COMPUTER-GENERATED, DO NOT EDIT BY HAND! */
#define SIZEOF_INT #{sizeof_int}
/* others can go here... */
HEADER

Then add a rule to your Makefile or other build script, which will make it run the above script to build sizes.h.

Include sizes.h wherever you need to use preprocessor conditionals based on sizes.

Done!

(Have you ever typed ./configure && make to build a program? What configure scripts do is basically just like the above...)

Alex D
  • 29,755
  • 7
  • 80
  • 126
4

What about next macro:

/* 
 * Simple compile time assertion.
 * Example: CT_ASSERT(sizeof foo <= 16, foo_can_not_exceed_16_bytes);
 */
#define CT_ASSERT(exp, message_identifier) \
    struct compile_time_assertion { \
        char message_identifier : 8 + !(exp); \
    }

For example in comment MSVC tells something like:

test.c(42) : error C2034: 'foo_can_not_exceed_16_bytes' : type of bit field too small for number of bits
Sergio
  • 8,099
  • 2
  • 26
  • 52
2

Just as a reference for this discussion, I report that some compilers get sizeof() ar pre-processor time.

JamesMcNellis answer is correct, but some compilers go through this limitation (this probably violates strict ansi c).

As a case of this, I refer to IAR C-compiler (probably the leading one for professional microcontroller/embedded programming).

  • 1
    Are you sure about that? IAR [claims](http://www.iar.com/en/Products/IAR-Embedded-Workbench/Technology/Language-standards/) that their compilers conform to the ISO C90 and C99 standards, which do not permit evaluation of `sizeof` at preprocessing time. `sizeof` should be treated as just an identifier. – Keith Thompson Aug 29 '13 at 14:58
  • 7
    In 1998, someone on the comp.std.c newsgroup wrote: "It was nice back in the days when things like `#if (sizeof(int) == 8)` actually worked (on some compilers)." The response: "Must have been before my time.", was from Dennis Ritchie. – Keith Thompson Aug 29 '13 at 15:01
  • Sorry for late reply... Yes, I am sure, I have working examples of code compiled for 8/16/32 bits microcontrollers, Renesas compilers (both R8 and RX). – graziano governatori Sep 26 '13 at 17:23
  • Actually, there should be some option for requiring "strict" ISO C – graziano governatori Sep 26 '13 at 17:24
  • its not a violation of the standard as long as the standard does not prohibit it. then i would call it a rare and non-standards feature - thus you will avoid it in regular cases for the sake of keeping compiler independence & platform portability. – Alexander Stohr Jan 14 '19 at 13:46
  • no. The sizeof is not preprocessed into the value of the size, it is handled as a string and gets evaluated when the compile occurs. There is NO compiler that will return the size as a value during or before preprocessing. Preprocessing is very literally string replacement. – Dan Sep 18 '19 at 21:05
1

#define SIZEOF(x) ((char*)(&(x) + 1) - (char*)&(x)) might work

  • This is an interesting solution however it works only with defined variables, not with types. Another solution that works with type but not with variables would be: `#define SIZEOF_TYPE(x) (((x*)0) + 1)` – greydet Sep 06 '13 at 08:50
  • 8
    It doesn't work because you still can't use its result within an `#if` condition. It provides no benefit over `sizeof(x)`. – interjay Oct 14 '13 at 07:58
1

In C11 _Static_assert keyword is added. It can be used like:

_Static_assert(sizeof(someThing) == PAGE_SIZE, "Data structure doesn't match page size")
cagatayo
  • 146
  • 1
  • 6
1

To check at compile time the size of data structures against their constraints I've used this trick.

#if defined(__GNUC__)
{ char c1[sizeof(x)-MAX_SIZEOF_X-1]; } // brakets limit c1's scope
#else
{ char c1[sizeof(x)-MAX_SIZEOF_X]; }   
#endif

If x's size is greater or equal than it's limit MAX_SIZEOF_X, then the gcc wil complain with a 'size of array is too large' error. VC++ will issue either error C2148 ('total size of array must not exceed 0x7fffffff bytes') or C4266 'cannot allocate an array of constant size 0'.

The two definitions are needed because gcc will allow a zero-sized array to be defined this way (sizeof x - n).

0

In my portable c++ code ( http://www.starmessagesoftware.com/cpcclibrary/ ) wanted to put a safe guard on the sizes of some of my structs or classes.

Instead of finding a way for the preprocessor to throw an error ( which cannot work with sizeof() as it is stated here ), I found a solution here that causes the compiler to throw an error. http://www.barrgroup.com/Embedded-Systems/How-To/C-Fixed-Width-Integers-C99

I had to adapt that code to make it throw an error in my compiler (xcode):

static union
{
    char   int8_t_incorrect[sizeof(  int8_t) == 1 ? 1: -1];
    char  uint8_t_incorrect[sizeof( uint8_t) == 1 ? 1: -1];
    char  int16_t_incorrect[sizeof( int16_t) == 2 ? 1: -1];
    char uint16_t_incorrect[sizeof(uint16_t) == 2 ? 1: -1];
    char  int32_t_incorrect[sizeof( int32_t) == 4 ? 1: -1];
    char uint32_t_incorrect[sizeof(uint32_t) == 4 ? 1: -1];
};
Mike
  • 436
  • 5
  • 9
  • 3
    Are you sure those “−1” will never be interpreted as 0xFFFF…FF, causing your program to request all the addressable memory? – Anton Samsonov Jun 23 '15 at 17:36
0

After trying out the mentioned macro's, this fragment seems to produce the desired result (t.h):

#include <sys/cdefs.h>
#define STATIC_ASSERT(condition) typedef char __CONCAT(_static_assert_, __LINE__)[ (condition) ? 1 : -1]
STATIC_ASSERT(sizeof(int) == 4);
STATIC_ASSERT(sizeof(int) == 42);

Running cc -E t.h:

# 1 "t.h"
...
# 2 "t.h" 2

typedef char _static_assert_3[ (sizeof(int) == 4) ? 1 : -1];
typedef char _static_assert_4[ (sizeof(int) == 42) ? 1 : -1];

Running cc -o t.o t.h:

% cc -o t.o t.h
t.h:4:1: error: '_static_assert_4' declared as an array with a negative size
STATIC_ASSERT(sizeof(int) == 42);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
t.h:2:84: note: expanded from macro 'STATIC_ASSERT'
  ...typedef char __CONCAT(_static_assert_, __LINE__)[ (condition) ? 1 : -1]
                                                       ^~~~~~~~~~~~~~~~~~~~
1 error generated.

42 isn't the answer to everything after all...

Coroos
  • 371
  • 2
  • 9
-10

The sizeof operator is not available for the preprocessor, but you can transfer sizeof to the compiler and check the condition in runtime:

#define elem_t double

#define compiler_size(x) sizeof(x)

elem_t n;
if (compiler_size(elem_t) == sizeof(int)) {
    printf("%d",(int)n);
} else {
    printf("%lf",(double)n);
}
Freeman
  • 5,810
  • 3
  • 47
  • 48
  • 15
    How does it improve on the already accepted answer? What purpose does defining `compiler_size` serve? What does your example try to show? – ugoren Jun 30 '13 at 10:04