Static assert in C

Viewed 105235

What's the best way to achieve compile time static asserts in C (not C++), with particular emphasis on GCC?

13 Answers

cl

I know the question explicitly mentions gcc, but just for completeness here is a tweak for Microsoft compilers.

Using the negatively sized array typedef does not persuade cl to spit out a decent error. It just says error C2118: negative subscript. A zero-width bitfield fares better in this respect. Since this involves typedeffing a struct, we really need to use unique type names. __LINE__ does not cut the mustard — it is possible to have a COMPILE_TIME_ASSERT() on the same line in a header and a source file, and your compile will break. __COUNTER__ comes to the rescue (and it has been in gcc since 4.3).

#define CTASTR2(pre,post) pre ## post
#define CTASTR(pre,post) CTASTR2(pre,post)
#define STATIC_ASSERT(cond,msg) \
    typedef struct { int CTASTR(static_assertion_failed_,msg) : !!(cond); } \
        CTASTR(static_assertion_failed_,__COUNTER__)

Now

STATIC_ASSERT(sizeof(long)==7, use_another_compiler_luke)

under cl gives:

error C2149: 'static_assertion_failed_use_another_compiler_luke' : named bit field cannot have zero width

Gcc also gives an intelligible message:

error: zero width for bit-field ‘static_assertion_failed_use_another_compiler_luke’

This answer vastly improved Apr. 17 2022, as an Easter gift. I believe it to be the most thorough and complete answer here, since I have produced 3 separate solutions of varying complexities to cover different versions of C and C++, and since the last version I present covers all versions of C and C++, which was initially a seemingly impossible feat.

You can view and test the code below for all versions of C and C++ in my file static_assert_for_all_versions_of_c_and_cpp.c.

Note that C++ style comments are not allowed in ISO C90, so my code samples must use only C-style comments (/* */), instead of C++-style // comments, in order for my code to be able to compile in -std=c90 as well.

Quick summary (TLDR) to obtain STATIC_ASSERT(test_for_true):

If you want a quick and super-simple macro to work in any version of C (when compiled with gcc), or in any version of C++ as of C++11 or later, see my two simple chunks of macros in the bottom of the very next section: "Summary of static assert declarations available in C and C++". Here are those macros copied and pasted for your convenience:

  1. [the simplest option by far!] STATIC_ASSERT(test_for_true) for only C11 or later and only C++11 or later:
    #include <assert.h>
    #define STATIC_ASSERT(test_for_true) \
        static_assert((test_for_true), "(" #test_for_true ") failed")
    
  2. Or [MY PREFERENCE], STATIC_ASSERT(test_for_true) for any version of C (as a gcc extension when using the gcc compiler), including C90, C99, C11, C17, etc., and for C++11 or later (cannot handle older versions of C++):
    #ifdef __cplusplus
        #ifndef _Static_assert
            #define _Static_assert static_assert
        #endif
    #endif
    #define STATIC_ASSERT(test_for_true) \
        _Static_assert((test_for_true), "(" #test_for_true ") failed")
    

If you want a single STATIC_ASSERT macro to work in all versions of C and C++, I present it in the section which begins with "My final version", below. You can see what build commands and language settings I used to test it in the "Summary of tests" section at the bottom. Getting a static assert to work in pre-C++11, such as C++98, C++03, etc, was the hard part! The _Static_assert_hack macro below is what handles those earlier versions of C++. Here is the full code chunk to handle all versions of C and C++, with most comments removed, for your convenience:

STATIC_ASSERT(test_for_true) for any version of C and any version of C++:

// See: https://stackoverflow.com/a/54993033/4561887

#define CONCAT_(prefix, suffix) prefix##suffix
#define CONCAT(prefix, suffix) CONCAT_(prefix, suffix)
#define MAKE_UNIQUE_VARIABLE_NAME(prefix) CONCAT(prefix##_, __LINE__)

/* Static assert hack required for **pre-C++11**, such as C++98, C++03, etc. */
/* - It works only with C++, NOT with C! */
#define _Static_assert_hack(expression, message) \
    struct MAKE_UNIQUE_VARIABLE_NAME(static_assertion_failed) \
    { \
        _Pragma("GCC diagnostic push") \
        _Pragma("GCC diagnostic ignored \"-Wunused-local-typedefs\"") \
        typedef char static_assertion_failed[(expression) ? 1 : -1]; \
        _Pragma("GCC diagnostic pop") \
    }

/* For C++ only: */
/* See: https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html */
#ifdef __cplusplus
    #if __cplusplus < 201103L
        /* for pre-C++11 */
        #ifndef _Static_assert
            #define _Static_assert _Static_assert_hack
        #endif
    #else
        /* for C++11 or later */
        #ifndef _Static_assert
            #define _Static_assert static_assert
        #endif
    #endif
#endif

/* For C **and** C++: */
#define STATIC_ASSERT(test_for_true) \
    _Static_assert((test_for_true), "(" #test_for_true ") failed")

Summary of static assert declarations available in C and C++:

Know that for the:

  1. C language: _Static_assert(expression, message) is available in C11 or later.
    1. Per the cppreference.com community wiki link above, static_assert is also available as a convenience macro to _Static_assert, in the header <assert.h>, in order to match the naming in C++11. So, to get the C++-like static_assert as a macro in C11 or later, you should also #include <assert.h>.
    2. _Static_assert(expression) (ie: without the message part) is also available as of C23 or later.
  2. C++ language: static_assert(expression, message) is available in C++11 or later.
    1. static_assert(expression) (ie: without the message part) is also available in C++17 or later.
  3. gcc compiler:
    1. As of gcc compiler version 4.6 and later, _Static_assert is supported as a gcc extension for all versions of C, including c90, c99, c11, c17, etc.
      1. And, per the C11 standard, as stated above, static_assert is avaialable as a macro to _Static_assert for C11 or later if you also #include <assert.h>.
    2. As of g++ compiler version 4.3 and later, static_assert is supported as a keyword for C++11 or later. You do NOT need to #include <assert.h> in C++ like you do in C to get this format.
    3. GCC source: https://www.gnu.org/software/gnulib/manual/html_node/assert_002eh.html (emphasis added):

    Even-older platforms do not support static_assert or _Static_assert at all. For example, GCC versions before 4.6 do not support _Static_assert, and G++ versions before 4.3 do not support static_assert, which was standardized by C11 and C++11.

    C _Static_assert and C++ static_assert are keywords that can be used without including <assert.h>. The Gnulib substitutes are macros that require including <assert.h>.

    1. See also: https://gcc.gnu.org/wiki/C11Status --I got this link from the main answer.

I like to write a STATIC_ASSERT wrapper macro to reduce the arguments down to 1 and automatically produce the message argument so I can do STATIC_ASSERT(expression) instead of STATIC_ASSERT(expression, message). Here is how to easily do that:

  1. For only C11 or later and only C++11 or later:
    #include <assert.h>
    #define STATIC_ASSERT(test_for_true) \
        static_assert((test_for_true), "(" #test_for_true ") failed")
    
  2. Or [MY PREFERENCE], for any version of C (as a gcc extension when using the gcc compiler), including C90, C99, C11, C17, etc., and for C++11 or later (cannot handle older versions of C++):
    #ifdef __cplusplus
        #ifndef _Static_assert
            #define _Static_assert static_assert
        #endif
    #endif
    #define STATIC_ASSERT(test_for_true) \
        _Static_assert((test_for_true), "(" #test_for_true ") failed")
    
  3. For versions of C++ older than C++11, you'll have to use a hack to obtain a functional static assert. Use my pretty intricate pre-C++11 static assert hack presented below, or, (even better!), just upgrade to C++11 or later.

Test the above code snippets here in my static_assert_for_all_versions_of_c_and_cpp.c.

Static assert hacks for non-gcc pre-C11, and for pre-C++11

1/2. For C only (ex: useful for non-gcc pre-C11)

For gcc pre-C11, gcc has already defined _Static_assert(expression, message), which is really nice. So, just use that and be done, as described above! But, what if you aren't using the gcc compiler though? What can you do?

Well, I noticed something really interesting. If I use _Static_assert(1 > 2, "this should fail"); in C90 with gcc, using this build command:

gcc -Wall -Wextra -Werror -O3 -std=c90 \
    static_assert_for_all_versions_of_c_and_cpp.c -o bin/a -lm && bin/a

I get this compile-time error for that failed _Static_assert. This is a super weird error! This is not an accidental build error though, this is the static assert failure error, because they are also using a hack for this version of C to get compile-time static assertions!

In file included from /usr/include/features.h:461,
                 from /usr/include/x86_64-linux-gnu/bits/libc-header-start.h:33,
                 from /usr/include/stdint.h:26,
                 from /usr/lib/gcc/x86_64-linux-gnu/9/include/stdint.h:9,
                 from static_assert_for_all_versions_of_c_and_cpp.c:73:
static_assert_for_all_versions_of_c_and_cpp.c: In function ‘main’:
static_assert_for_all_versions_of_c_and_cpp.c:224:5: error: negative width in bit-field ‘__error_if_negative’
  224 |     _Static_assert(1 > 2, "this should fail");
      |     ^~~~~~~~~~~~~~

If I go to the gcc source code mirror on GitHub here (https://github.com/gcc-mirror/gcc), clone the repo, and then search for __error_if_negative using grep or ripgrep I find a result in only one location, here: https://github.com/gcc-mirror/gcc/blob/master/libgcc/soft-fp/soft-fp.h#L69-L71:

# define _FP_STATIC_ASSERT(expr, msg)                   \
  extern int (*__Static_assert_function (void))             \
    [!!sizeof (struct { int __error_if_negative: (expr) ? 2 : -1; })]

This is a static assertion hack you can borrow and use in non-gcc versions of pre-C11 C!

Just replace _FP_STATIC_ASSERT with _Static_assert, like this:

# define _Static_assert(expr, msg)                   \
  extern int (*__Static_assert_function (void))             \
    [!!sizeof (struct { int __error_if_negative: (expr) ? 2 : -1; })]

Caveats of using the _Static_assert hack just above:

  1. It only works in C, not in C++!
  2. It does not work inside structs or unions in ISO C--ie: when you use -std=c90, -std=c99, etc.
    1. It does work, I believe, if you use the gnu C language, such as -std=gnu90 or -std=gnu99, however.
    2. If you try to use it inside a union or struct like this:
      typedef union data_u
      {
          data_t data;
          uint8_t bytes[sizeof(data_t)];
      
          _Static_assert(2 > 1, "this should pass");
          _Static_assert(5 > 4, "this should pass");
      } data_union_t;
      
      ...then you'll see this super cryptic error about expected specifier-qualifier-list before ‘extern’. This is not because the static assertion expression failed (was false), but rather it is because the static assertion hack is broken in this use-case. Notice that extern is used in the hack above, so, it shows up in the error:
      eRCaGuy_hello_world/c$ gcc -Wall -Wextra -Werror -O3 -std=c90 static_assert_for_all_versions_of_c_and_cpp.c -o bin/a -lm && bin/a
      In file included from /usr/include/features.h:461,
                       from /usr/include/x86_64-linux-gnu/bits/libc-header-start.h:33,
                       from /usr/include/stdint.h:26,
                       from /usr/lib/gcc/x86_64-linux-gnu/9/include/stdint.h:9,
                       from static_assert_for_all_versions_of_c_and_cpp.c:73:
      static_assert_for_all_versions_of_c_and_cpp.c:193:5: error: expected specifier-qualifier-list before ‘extern’
        193 |     _Static_assert(2 > 1, "this should pass");
            |     ^~~~~~~~~~~~~~
      

2/2. For C++ only (ex: useful for pre-C++11)

I found it very tricky to get a nice static assertion hack working in pre-C++11 C++, but I got one working! It's quite the work-of-art, but it does appear to work, to work well, and to be robust. It also does work inside structs and unions just like C++11's static_assert does! Here it is. You can test it here in my static_assert_for_all_versions_of_c_and_cpp.c:

#define CONCAT_(prefix, suffix) prefix##suffix
#define CONCAT(prefix, suffix) CONCAT_(prefix, suffix)
#define MAKE_UNIQUE_VARIABLE_NAME(prefix) CONCAT(prefix##_, __LINE__)

/* Static assert hack required for **pre-C++11**, such as C++98, C++03, etc. */
/* - It works only with C++, NOT with C! */
#define _Static_assert_hack(expression, message) \
    struct MAKE_UNIQUE_VARIABLE_NAME(static_assertion_failed) \
    { \
        _Pragma("GCC diagnostic push") \
        _Pragma("GCC diagnostic ignored \"-Wunused-local-typedefs\"") \
        typedef char static_assertion_failed[(expression) ? 1 : -1]; \
        _Pragma("GCC diagnostic pop") \
    }

My final version: a single STATIC_ASSERT() which works with all versions of C and all versions of C++, when compiled with gcc

With just a few tweaks to change which style is used and when, the below code could be made to work with any version of C and C++ on non-gcc compilers, too.

As it is written, I expect it to work for all versions of C and gnu C and all versions of C++ and gnu++ when compiled with either the gcc/g++ compiler or the LLVM clang compiler.

Here is the final version: one static assert to handle any version of C or C++!:

/* --------------------------------- START ---------------------------------- */
/* OR [BEST], for **any version of C OR C++**: */

/* See: https://stackoverflow.com/a/71899854/4561887 */
#define CONCAT_(prefix, suffix) prefix##suffix
/* Concatenate `prefix, suffix` into `prefixsuffix` */
#define CONCAT(prefix, suffix) CONCAT_(prefix, suffix)
/* Make a unique variable name containing the line number at the end of the */
/* name. Ex: `uint64_t MAKE_UNIQUE_VARIABLE_NAME(counter) = 0;` would */
/* produce `uint64_t counter_7 = 0` if the call is on line 7! */
#define MAKE_UNIQUE_VARIABLE_NAME(prefix) CONCAT(prefix##_, __LINE__)

/* Static assert hack required for **pre-C++11**, such as C++98, C++03, etc. */
/* - It works only with C++, NOT with C! */
/* See: */
/* 1. [my ans with this] https://stackoverflow.com/a/54993033/4561887 */
/* 1. Info. on `_Pragma()`: https://stackoverflow.com/a/47518775/4561887 */
/* 1. The inspiration for this `typedef char` array hack as a struct  */
/*    definition: https://stackoverflow.com/a/3385694/4561887 */
/* Discard the `message` portion entirely. */
#define _Static_assert_hack(expression, message) \
    struct MAKE_UNIQUE_VARIABLE_NAME(static_assertion_failed) \
    { \
        _Pragma("GCC diagnostic push") \
        _Pragma("GCC diagnostic ignored \"-Wunused-local-typedefs\"") \
        typedef char static_assertion_failed[(expression) ? 1 : -1]; \
        _Pragma("GCC diagnostic pop") \
    }

/* For C++ only: */
/* See: https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html */
#ifdef __cplusplus
    #if __cplusplus < 201103L
        /* for pre-C++11 */
        #ifndef _Static_assert
            #define _Static_assert _Static_assert_hack
        #endif
    #else
        /* for C++11 or later */
        #ifndef _Static_assert
            #define _Static_assert static_assert
        #endif
    #endif
#endif

/* For C **and** C++: */
#define STATIC_ASSERT(test_for_true) \
    _Static_assert((test_for_true), "(" #test_for_true ") failed")
/* ---------------------------------- END ----------------------------------- */

The references I used to help me build this up are in the comments in the source code above. Here are the clickable links copied from there:

  1. [my answer] How to auto-generate unique variable names with the line number in them by using macros
    1. I learned this primarily from @Jarod42 here, but also from @Adam.Rosenfield here.
  2. Info. on _Pragma(): How to disable GCC warnings for a few lines of code
  3. The inspiration for this typedef char array hack as a struct definition:
    1. Answer by @Nordic.Mainframe
    2. gcc source code static assert hack
  4. gcc predefined macros:
    1. https://gcc.gnu.org/onlinedocs/cpp/Standard-Predefined-Macros.html
    2. https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html

C++03 sample static assert error output:

Using the above STATIC_ASSERT in a pre-C++11 use-case, here is some sample code with a static assert which is expected to fail since it is false:

typedef union data_u
{
    data_t data;
    uint8_t bytes[sizeof(data_t)];

    STATIC_ASSERT(2 > 2); /* this should fail */
} data_union_t;

Here is what the build command and failing output looks like. It's a bit of a mouthful of errors for one failed static assert in C++, but this is to be expected for hacks like this, and the gcc C90 hack for _Static_assert, presented previously above, wasn't any better:

eRCaGuy_hello_world/c$ g++ -Wall -Wextra -Werror -O3 -std=c++03 static_assert_for_all_versions_of_c_and_cpp.c -o bin/a && bin/a
static_assert_for_all_versions_of_c_and_cpp.c:129:67: error: narrowing conversion of ‘-1’ from ‘int’ to ‘long unsigned int’ is ill-formed in C++11 [-Werror=narrowing]
  129 |         typedef char static_assertion_failed[(expression) ? 1 : -1]; \
      |                                                                   ^
static_assert_for_all_versions_of_c_and_cpp.c:139:36: note: in expansion of macro ‘_Static_assert_hack’
  139 |             #define _Static_assert _Static_assert_hack
      |                                    ^~~~~~~~~~~~~~~~~~~
static_assert_for_all_versions_of_c_and_cpp.c:151:5: note: in expansion of macro ‘_Static_assert’
  151 |     _Static_assert((test_for_true), "(" #test_for_true ") failed")
      |     ^~~~~~~~~~~~~~
static_assert_for_all_versions_of_c_and_cpp.c:187:5: note: in expansion of macro ‘STATIC_ASSERT’
  187 |     STATIC_ASSERT(2 > 2);
      |     ^~~~~~~~~~~~~
static_assert_for_all_versions_of_c_and_cpp.c:129:59: error: size ‘-1’ of array ‘static_assertion_failed’ is negative
  129 |         typedef char static_assertion_failed[(expression) ? 1 : -1]; \
      |                                              ~~~~~~~~~~~~~^~~~~~~~
static_assert_for_all_versions_of_c_and_cpp.c:139:36: note: in expansion of macro ‘_Static_assert_hack’
  139 |             #define _Static_assert _Static_assert_hack
      |                                    ^~~~~~~~~~~~~~~~~~~
static_assert_for_all_versions_of_c_and_cpp.c:151:5: note: in expansion of macro ‘_Static_assert’
  151 |     _Static_assert((test_for_true), "(" #test_for_true ") failed")
      |     ^~~~~~~~~~~~~~
static_assert_for_all_versions_of_c_and_cpp.c:187:5: note: in expansion of macro ‘STATIC_ASSERT’
  187 |     STATIC_ASSERT(2 > 2);
      |     ^~~~~~~~~~~~~
cc1plus: all warnings being treated as errors

Summary of tests

See static_assert_for_all_versions_of_c_and_cpp.c.

The final STATIC_ASSERT(test_for_true) macro I present just above, which handles all versions of C and C++, was tested on Linux Ubuntu 20.04 with gcc compiler version (gcc --version) 9.4.0 (gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0).

Here are the various build commands and languages for which it is tested and works. Again, out of all of these versions, the only ones which do not allow the STATIC_ASSERT() macro to be used inside of structs and unions are -std=c90 and -std=c99! All of the other options support the usage of STATIC_ASSERT wherever you want, including outside of functions, inside of functions, and inside of structs and unions.

# -------------------
# 1. In C:
# -------------------

gcc -Wall -Wextra -Werror -O3 -std=c90 \
    static_assert_for_all_versions_of_c_and_cpp.c -o bin/a -lm && bin/a
gcc -Wall -Wextra -Werror -O3 -std=c99 \
    static_assert_for_all_versions_of_c_and_cpp.c -o bin/a -lm && bin/a
gcc -Wall -Wextra -Werror -O3 -std=c11 \
    static_assert_for_all_versions_of_c_and_cpp.c -o bin/a -lm && bin/a
gcc -Wall -Wextra -Werror -O3 -std=c17 \
    static_assert_for_all_versions_of_c_and_cpp.c -o bin/a -lm && bin/a

# gnu C
gcc -Wall -Wextra -Werror -O3 -std=gnu90 \
    static_assert_for_all_versions_of_c_and_cpp.c -o bin/a -lm && bin/a
gcc -Wall -Wextra -Werror -O3 -std=gnu99 \
    static_assert_for_all_versions_of_c_and_cpp.c -o bin/a -lm && bin/a
gcc -Wall -Wextra -Werror -O3 -std=gnu11 \
    static_assert_for_all_versions_of_c_and_cpp.c -o bin/a -lm && bin/a
# [my default C build cmd I use today]:
gcc -Wall -Wextra -Werror -O3 -std=gnu17 \
    static_assert_for_all_versions_of_c_and_cpp.c -o bin/a -lm && bin/a  

# -------------------
# 2. In C++
# -------------------

g++ -Wall -Wextra -Werror -O3 -std=c++98 \
    static_assert_for_all_versions_of_c_and_cpp.c -o bin/a && bin/a
g++ -Wall -Wextra -Werror -O3 -std=c++03 \
    static_assert_for_all_versions_of_c_and_cpp.c -o bin/a && bin/a
g++ -Wall -Wextra -Werror -O3 -std=c++11 \
    static_assert_for_all_versions_of_c_and_cpp.c -o bin/a && bin/a
g++ -Wall -Wextra -Werror -O3 -std=c++17 \
    static_assert_for_all_versions_of_c_and_cpp.c -o bin/a && bin/a
# gnu++
g++ -Wall -Wextra -Werror -O3 -std=gnu++98 \
    static_assert_for_all_versions_of_c_and_cpp.c -o bin/a && bin/a
g++ -Wall -Wextra -Werror -O3 -std=gnu++03 \
    static_assert_for_all_versions_of_c_and_cpp.c -o bin/a && bin/a
g++ -Wall -Wextra -Werror -O3 -std=gnu++11 \
    static_assert_for_all_versions_of_c_and_cpp.c -o bin/a && bin/a
# [my default C++ build cmd I use today]:
g++ -Wall -Wextra -Werror -O3 -std=gnu++17 \
    static_assert_for_all_versions_of_c_and_cpp.c -o bin/a && bin/a  

Related:

  1. Use static_assert to check types passed to macro [my own answer]
    1. https://en.cppreference.com/w/cpp/types/is_same
    2. https://en.cppreference.com/w/cpp/language/decltype
  2. Use static_assert to check types passed to macro
  3. How to use static assert in C to check the types of parameters passed to a macro

I would NOT recommend using the solution using a typedef:

// Do NOT do this
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]

The array declaration with typedef keyword is NOT guaranteed to be evaluated at compile time. For example, the following code in block scope will compile:

int invalid_value = 0;
STATIC_ASSERT(invalid_value, this_should_fail_at_compile_time_but_will_not);

I would recommend this instead (on C99):

// Do this instead
#define STATIC_ASSERT(COND,MSG) static int static_assertion_##MSG[(COND)?1:-1]

Because of the static keyword, the array will be defined at compile time. Note that this assert will only work with COND which are evaluated at compile time. It will not work with (i.e. the compile will fail) with conditions that are based on values in memory, such as values assigned to variables.

From Perl, specifically perl.h line 3455 (<assert.h> is included beforehand):

/* STATIC_ASSERT_DECL/STATIC_ASSERT_STMT are like assert(), but for compile
   time invariants. That is, their argument must be a constant expression that
   can be verified by the compiler. This expression can contain anything that's
   known to the compiler, e.g. #define constants, enums, or sizeof (...). If
   the expression evaluates to 0, compilation fails.
   Because they generate no runtime code (i.e.  their use is "free"), they're
   always active, even under non-DEBUGGING builds.
   STATIC_ASSERT_DECL expands to a declaration and is suitable for use at
   file scope (outside of any function).
   STATIC_ASSERT_STMT expands to a statement and is suitable for use inside a
   function.
*/
#if (defined(static_assert) || (defined(__cplusplus) && __cplusplus >= 201103L)) && (!defined(__IBMC__) || __IBMC__ >= 1210)
/* static_assert is a macro defined in <assert.h> in C11 or a compiler
   builtin in C++11.  But IBM XL C V11 does not support _Static_assert, no
   matter what <assert.h> says.
*/
#  define STATIC_ASSERT_DECL(COND) static_assert(COND, #COND)
#else
/* We use a bit-field instead of an array because gcc accepts
   'typedef char x[n]' where n is not a compile-time constant.
   We want to enforce constantness.
*/
#  define STATIC_ASSERT_2(COND, SUFFIX) \
    typedef struct { \
        unsigned int _static_assertion_failed_##SUFFIX : (COND) ? 1 : -1; \
    } _static_assertion_failed_##SUFFIX PERL_UNUSED_DECL
#  define STATIC_ASSERT_1(COND, SUFFIX) STATIC_ASSERT_2(COND, SUFFIX)
#  define STATIC_ASSERT_DECL(COND)    STATIC_ASSERT_1(COND, __LINE__)
#endif
/* We need this wrapper even in C11 because 'case X: static_assert(...);' is an
   error (static_assert is a declaration, and only statements can have labels).
*/
#define STATIC_ASSERT_STMT(COND)      STMT_START { STATIC_ASSERT_DECL(COND); } STMT_END

If static_assert is available (from <assert.h>), it is used. Otherwise, if the condition is false, a bit-field with a negative size is declared, which causes compilation to fail.

STMT_START / STMT_END are macros expanding to do / while (0), respectively.

For C versions older than C11, it is possible to build your own static assert. The following is tested on old versions of GCC.

Of course, if you can use C11, then it makes the best sense to #include <assert.h> and use static_assert.

/** @file
 * STATIC_ASSERT allows you to do compile time assertions at file scope or in a function.
 * @param expr: a boolean expression that is valid at compile time.
 * @param msg: a "message" that must also be a valid identifier, i.e. message_with_underscores
 */

#ifdef __GNUC__
#define STATIC_ASSERT_HELPER(expr, msg) \
    (!!sizeof(struct { unsigned int STATIC_ASSERTION__##msg: (expr) ? 1 : -1; }))
#define STATIC_ASSERT(expr, msg) \
    extern int (*assert_function__(void)) [STATIC_ASSERT_HELPER(expr, msg)]
#else
    #define STATIC_ASSERT(expr, msg)   \
    extern char STATIC_ASSERTION__##msg[1]; \
    extern char STATIC_ASSERTION__##msg[(expr)?1:2]
#endif /* #ifdef __GNUC__ */

#define STATIC_ASSERT_ARRAY_LEN(array, len) \
    STATIC_ASSERT(sizeof(array)/sizeof(array[0]) == len, array##_wrong_size);

#endif // STATIC_ASSERT_H

The idea is essentially the same as in Hashbrown's answer, except I have the array helper and a special case for gnuc.

Source: https://github.com/apache/qpid-dispatch/blob/f2e205c733558102006ed6dd0a44453c9821c80a/include/qpid/dispatch/static_assert.h#L23-L44

This worked for some old gcc. Sorry that I forgot what version it was:

#define _cat(x, y) x##y

#define _sassert(exp, ln)\
extern char _cat(SASSERT_, ln)[1]; \
extern char _cat(SASSERT_, ln)[exp ? 1 : 2]

#define sassert(exp) _sassert((exp), __LINE__)

//
sassert(1 == 2);

//
#148 declaration is incompatible with "char SASSERT_134[1]" (declared at line 134)  main.c  /test/source/controller line 134    C/C++ Problem
Related