# tonfa: 18 Sep 2008
If you want to look at the linux kernel version of that:
http://lxr.linux.no/linux+v2.6.26.5/include/linux/kernel.h#L494
# Pádraig Brady: 18 Sep 2008
cool. So using that method:

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

When that fires it gives:

file.c:16: error: size of array 'type name' is negative

Whereas the one on the web page gives:

file.c:16: warning: division by zero
file.c:16: error: enumerator value for 'assert_line_16' is not an integer constant
# kL: 18 Sep 2008
Take a look at D programming language - it has static asserts and even unit testing at language level (and it's as fast and low-level as C)
# Keith Thompson: 18 Sep 2008
The trick of enclosing the enum definition in braces:

#define ct_assert(e) {enum { ct_assert_value = 1/(!!(e)) };}

is a good one, but it's still not completely general. Without the braces, it's a declaration; with the braces, it's a statement, specifically a compound statement.

C99 (which is not widely implemented) allows declarations and statements to be mixed, so the braces aren't necessary if you're using a C99 compiler, or a C++ compiler, or a pre-C99 compiler that allows mixed declarations and statements as an extension.

But in strict C90, all declarations in a block must be followed by all statements in a block; they can't be mixed. So if the version with the braces is used within the declaration part of a block (specifically, if it's followed by a declaration at the same level), then it's an error.

A workaround is to define two versions of the macro, one without the braces for use in a declaration context, and another with the braces for use in a statement context.
# Bill davy: 14 Oct 2008
And if it is placed on the same line number in two different files ...

Still, very useful.
# Pádraig Brady: 14 Oct 2008
@Bill good point.

If these files are in the same compilation unit you'll get an error. For e.g. you could hit this if you had multiple header files using ct_assert() on the same line, as confirmed by:

echo "enum { a = 1 }; enum { a = 1 };" | gcc -xc -

I'll look at putting __FILE__ or some other distinguishing id in there.

thanks!
# Bill Davy: 16 Oct 2008
__COUNTER__ on VS could help but even that does not guarantee uniqueness as it always starts in the same place after a pre-compiled header.
# Bill Davy: 20 Oct 2008
Think I'll stick with the old one. VS gets too excited!

d:\work\dmm1_0\fe_dhs\devapp\dmm11\shareddata.h(32) : error C2148: total size of array must not exceed 0x7fffffff bytes
d:\work\dmm1_0\fe_dhs\devapp\dmm11\shareddata.h(32) : error C2148: total size of array must not exceed 0x7fffffff bytes
d:\work\dmm1_0\fe_dhs\devapp\dmm11\shareddata.h(32) : error C2070: 'char [-1]': illegal sizeof operand
d:\work\dmm1_0\fe_dhs\devapp\dmm11\shareddata.h(32) : error C2466: cannot allocate an array of constant size 0
d:\work\dmm1_0\fe_dhs\devapp\dmm11\shareddata.h(55) : error C2556: 'char (*ct_assert(void))[1]' : overloaded function differs only by return type from 'char (*ct_assert(void))[]'
d:\work\dmm1_0\fe_dhs\devapp\dmm11\shareddata.h(32) : see declaration of 'ct_assert'
d:\work\dmm1_0\fe_dhs\devapp\dmm11\shareddata.h(55) : error C2369: 'ct_assert' : redefinition; different subscripts
d:\work\dmm1_0\fe_dhs\devapp\dmm11\shareddata.h(32) : see declaration of 'ct_assert'
d:\work\dmm1_0\fe_dhs\devapp\dmm11\shareddata.h(227) : error C2556: 'char (*ct_assert(void))[1]' : overloaded function differs only by return type from 'char (*ct_assert(void))[]'
d:\work\dmm1_0\fe_dhs\devapp\dmm11\shareddata.h(32) : see declaration of 'ct_assert'
d:\work\dmm1_0\fe_dhs\devapp\dmm11\shareddata.h(341) : error C2556: 'char (*ct_assert(void))[1]' : overloaded function differs only by return type from 'char (*ct_assert(void))[]'
d:\work\dmm1_0\fe_dhs\devapp\dmm11\shareddata.h(32) : see declaration of 'ct_assert'
d:\work\dmm1_0\fe_dhs\devapp\dmm11\stdafx.h(205) : error C2556: 'char (*ct_assert(void))[1]' : overloaded function differs only by return type from 'char (*ct_assert(void))[]'
d:\work\dmm1_0\fe_dhs\devapp\dmm11\shareddata.h(32) : see declaration of 'ct_assert'
# Jesse Chisholm: 07 Apr 2009
In the Microsoft compiler (VS.2008) the expression (1/!!(e)) generates a warning. It must implicitly cast bool to int. An alternate expression, that is only slightly uglier is (1/((e)?1:0)) which has the same effect, but removes the implicit cast.

-Jesse
# Pádraig Brady: 07 Apr 2009
Argh! Thanks Jesse. So it's giving a warning about dividing an int with a bool? Could I add a cast instead? I'm wary about adding conditionals.
# Brent: 08 Nov 2011
I like it! Thanks!
# stephen.pitts3@gmail.com: 08 Nov 2012
Adding a statc assert of the non-C11 style immediately after a case statement causes an erro (cannot use label.....) in CCS5.
Consequently the macro requires an extra brace at the start and a semi colon and brace at the end.
# Pádraig Brady: 08 Nov 2012
Updated. Thanks!
# serhio: 17 Oct 2013
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
# Robin Hsu: 29 Dec 2014
The double brace version of STATIC_ASSERT(e,m) still not work, if it is placed at the global variable scope. For example, when it is used in the following codes:

STATIC_ASSERT(sizeof(int)==4, "");

int main(int, argc**)
{
return 0;
}

For C++, wrapping your enum {} by a uniquely named struct/class would solve the problem. I am not quite sure how to do it in C.

# Pádraig Brady: 29 Dec 2014
Yes as stated in the article, for traditional C, i.e. compilers not supporting the 15 year old C99 standard you need separate macros for global scope.
# Robin Hsu: 06 Feb 2015
My idea is to use typedef, like this:

#define STATIC_ASSERT(e) \
typedef int _static_assert_type_[1/(!!(e))]

The beauty of this idea is that typedef a same name multiple times to a same type is not an error under C99. You won't need a unique identifier. And typedef can exist in almost any scope. The drawback of this idea is, when assert is raised, it raises two errors: 1. divided by zero, 2. variably modified typedef (i.e. typedef mismatches for a same name).

# Pádraig Brady: 06 Feb 2015
@Robin this is nearly very nice, but there are 2 issues.

1. If you have multiple STATIC_ASSERTS in the same scope, gcc 4.9 at least will give:
error: redefinition of typedef ‘_static_assert_type_’ with different type

2. Even if you only use the assert once per scope, with -Wall gcc will give:
warning: typedef ‘_static_assert_type_’ locally defined but not used [-Wunused-local-typedefs]
# Daniel: 26 Feb 2015
Thanks man, I really don't know how to do this stuff, and I was proving several ways, trying to mix some ideas by the web, but the simple macros resources that you free really helped me with my work.
# Roy Harrison: 23 Apr 2015
Just to say: "Thank you to all contributors" for providing some really useful code!
# Fang3s: 23 May 2016
Using VS 12.0.21005.1 REL, it gives `warning C4804: 'operation' : unsafe use of type 'bool' in operation`. Thus, for supressing the warnings, `(int)(!!(e))` will slience it.
# Pádraig Brady: 23 May 2016
Thanks @Fang3s. Updated.
# Vikram: 04 Aug 2016
With Visual Studio 2015 Update 1, the line

"enum { foo = 1/0 };"

does not throw a compiler error. Hence the assert doesn't fail when it's supposed to fail.
# Pádraig Brady: 04 Aug 2016
@Vikram, that sounds like a compiler bug? What value does foo have then?
# Vikram: 04 Aug 2016
Value is 0 !! No idea how.

I have filed a bug report - http://connect.microsoft.com/VisualStudio/feedback/details/3001747/compiler-error-c2057-not-happening-when-its-expected-to

Also, I just noticed that static_assert works in C code in VS 2012 and 2015. May be they added some C11 support?
# Mavik: 14 Feb 2017
Could you provide an example of a non-constant expression that the the GNU version in update 5 does not detect. Thanks :)
# David Chamberlain: 18 Jan 2018
Update 11 doesn't work where an expression is required. So e.g. the follow doesn't work:

#define F(x) (STATIC_ASSERT(0xFF >= x), f((uint8_t)x))

because the comma operator needs expressions not statements.

The kernel version:

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

is an expression, so it would work in this case, but alas it doesn't work at file scope. It would be nice to have one macro that works everywhere. Any ideas?
Name:
Website:
comments:
(no HTML)
31+9