This is a followup to a question about printing a common member from different structs
I thought that unions permit to examine the common initial sequence of two of their elements. So I ended with the following code:
#include <stdio.h>
struct foo {
const char *desc;
float foo;
};
struct bar {
const char *desc;
int bar;
};
union foobar {
struct foo foo;
struct bar bar;
};
void printdesc(const union foobar * fb) {
printf("%s\n", fb->foo.desc); // allowed per 6.5.2.3 Structure and union members
}
int main() {
struct bar bb = {"desc bar", 2};
union foobar fb = { .bar=bb};
printdesc((union foobar *) &(fb.bar)); // allowed per 6.7.2.1 Structure and union specifiers
printdesc((union foobar *) &bb); // legal?
return 0;
}
It compiles without even a warning and gives the expected result
desc bar
desc bar
The point here is the line with the // legal? comment. I have converted a bar *
into a foobar *
. When the bar
in a member of a foobar
union, it is permitted per 6.7.2.1 Structure and union specifiers. But here I do not know.
Is it permitted to convert a pointer to a bar
object to a pointer to a foobar
object if the bar
was not declared as a member of a foobar
?
The question is not about whether it can work in a specific compiler. I am pretty sure that it does with all the common compilers in their current versions. The question is about whether it is legal C code.
Here is my current research.
References from draft n1570 for C11:
6.5.2.3 Structure and union members § 6
... if a union contains several structures that share a common initial sequence (see below), and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them...
6.7.2.1 Structure and union specifiers § 16
... A pointer to a union object, suitably converted, points to each of its members ..., and vice versa...
fb->foo.desc
is not an access through a union type. – Nubblyunion U { Member1 m1; Member2 m2; }
. Further, givenunion U *up;
, the 'suitably converted' comment means thatMember1 *mp1 = (Member1 *)up;
is well defined, and so isMember2 *mp2 = (Member2 *)up;
. TryingOtherType *otp = (OtherType *)up;
is not 'suitably converted', nor isMember1 *mp1 = (Member2 *)up;
— though the chances are that the result is the same for the latter, even though it is not 'suitably converted'. – Gelid