Consider the following code:
const int g_foo = 1;
// (1):
_Static_assert(g_foo > 0, "g_foo > 0"); // error: expression in static assertion is not constant.
_Static_assert(g_foo > 2, "g_foo > 2"); // error: expression in static assertion is not constant.
void Foo(void) {
const int foo = 1;
// (2):
_Static_assert(foo > 0, "foo > 0"); // No issue.
_Static_assert(foo > 2, "foo > 2"); // error: static assertion failed: "foo > 2"
// (3):
_Static_assert(g_foo > 0, "g_foo > 0"); // No issue.
_Static_assert(g_foo > 2, "g_foo > 2"); // error: static assertion failed: "g_foo > 2"
}
Compiling using gcc -std=c11 -O3 test.c
with GCC version 7.4.0 produces the indicated error messages. A Compiler Explorer example using GCC 9.2 can also be found here.
Beginning at the static asserts labeled with (2), we are using a const-qualified local variable. At this optimization level, const int foo = 1;
is optimized out and, consequently, the initializer is not evaluated. From what I understand, this classifies it as a constant-expression according to 6.6.3 of ISO/IEC 9899:2011* and allows it to be evaluated by _Static_assert
**. So far so good.
The static asserts labeled with (1) attempt to evaluate expressions containing the global variable g_foo
, but the fact that globals are allocated space in memory means that they actually need to be initialized. This evaluation automatically disqualifies them from being a constant-expression and GCC complains accordingly.
Then we get to (3). Suddenly, the static asserts that failed in the global scope start working. What's going on?
* I'm unfortunately using the 2010-12-02 committee draft instead of the final published spec.
** As an interesting aside, the use of a nonzero optimization level is important in this question. With -O0
, the variable foo
actually gets instantiated with a literal 1
. However, this is no longer a constant-expression and the static asserts using foo
start failing with "expression in static assertion is not constant."
Edits (2019-11-04):
- Removed
*(int *)&g_foo = -1;
from the code block - it's undefined behavior and distracts from the main question. - Removed a superfluous quip about casting
const
qualified variables in C. - Added a link to Compiler Explorer to help with reproducing the issue.