Let's start immediately with the example code:
#include <stdio.h>
#include <stdbool.h>
typedef union {
bool b;
int i;
} boolIntUnion;
int main(void) {
boolIntUnion val;
val.i = 0;
printf("i = %d; %s\n", val.i, ((val.b) ? "true" : "false"));
val.i = 1;
printf("i = %d; %s\n", val.i, ((val.b) ? "true" : "false"));
val.i = 2;
printf("i = %d; %s\n", val.i, ((val.b) ? "true" : "false"));
}
My question now if using the union as you can see here is some sort of undefined behaviour. What's of interest for me is the third case, where I'm setting val.i
to 2
, but then use the boolean from the union via val.b
as a condition for my printed string. In MSVC 19 and gcc, I get the expected behaviour of the 2
in the union resulting in a value of true
from val.b
, but with Clang (and Clang-cl), I get false
. I'm suspecting a compiler error, because if I look at the assembly output of Clang, I see test cl, 1
, whereas MSVC and gcc give me test eax, eax
and test al, al
respectively. I tested this on Godbolt with all three compilers including execution output. Clang in the middle is where you can see the differing behaviour. [1]
Now, I'm not sure if using a union in this way is undefined behaviour under any C or C standard. That's basically my question now. If this is valid code, which I assume, I'd file a bug with Clang / LLVM. Just wanted to be sure beforehand. I tested this with both Clang and Clang , same behaviour.
[1] https://godbolt.org/z/c95eWa9fq
CodePudding user response:
In C , using a union member other than the last-stored member is undefined behavior, with an exception for structures with common initial layouts.
C 2020 draft N4849 [class.union] (11.5) 2 says “… At most one of the non-static data members of an object of union type can be active at any time…” with a note there is an exception for inspecting the members in structures in the union that have a common initial sequence. [basic.life] (6.7.3) 1.5 says the lifetime of an object o ends when (among other possibilities) “the storage which the object occupies … is reused by an object that is not nested within o (6.7.2)”. Thus, after val.i = …;
, the lifetime of val.b
has ended (if it began), and the behavior of accessing val.b
is not defined by the C standard. Any output or other behavior from the program or compiler is allowed by the C standard.
In C, accessing a union member other than the last-stored member results in reinterpreting the applicable bytes of the union in the new type, per C 2018 note 99 (speaking about 6.5.2.3 3, which says that accessing a union with .
or ->
provides the value of the named member, without exception for whether it was the last-stored member or not).