The dangers of the 'const' qualifier
“Undefined Behavior” is one of those frustrating aspects of the C Language. It leads to defensive programming and avoiding some of the coolest features of the language. Well, today I was struct, again, by such "undefinitions"...
“Undefined Behavior” is one of those frustrating aspects of the C Language. It leads to defensive programming and avoiding some of the coolest features of the language. Well, today I was struct, again, by such "undefinitions"...
A local declaration looked like:
struct some_struct * const var;
some_method(&var);
assert(var);
// do stuff with var...
… where some_method
initialized var
;
All fine. GCC and CLang were happy with that, a constant whose initialization was postponed to a function call.
But, one day, I had to use the ICC (Intel C++ Compiler) and it complained about this uninitialized declaration. So I absent-mindedly “fixed” it into:
strruct some_struct * const var = NULL;
some_method(&var);
assert(var);
// do stuff with var...
It built fine, no errors, no warnings. Unit tests passed. Integration tests passed. So I pushed.
Minutes later, the CI dashboard gets red! Unit tests failed on Release build!
Of course, I only tested the Debug build…
I made a RelWithDebInfo (Release with Debug Information) build and put GDB to work. As expected, it skipped the assert()
call because it is dropped on Release builds, and the code below was getting a NULL in var. Checking some_method
showed no way that var
could be set to NULL. Where was that NULL coming from? The initialization.
In GCC, when compiling using Release mode, we use the -O3
switch. With this switch on, it seems that GCC optimizes out assignments to const
values if they are initialized. So when I changed the declaration to include an initialization, GCC prevented the variable from being properly assigned. Of course, all those decisions are taken with NO WARNING issued.
Easy “solution”: to remove the const
qualifier:
struct some_struct *var = NULL;
some_method(&var);
assert(var);
// do stuff with var...
And now everything was again working… with all compilers.