> #define union struct /* keeps code correct, just wastes some space */
Not always, though... > #define union struct /* keeps code correct, just wastes some space */
Not always, though...Probably about the only place you'll see them used like that is in the code produced by web2c in compiling TeX. Knuth used variant records a lot to get around Pascal's type safety, and they get translated to unions.
The only standard compliant way to, say, convert a float to an int is to use memmove:
uint32 i;
float32 f;
i = 0x80000000;
memmove(&f, &i, 4); typedef union
{
#ifdef TeX
glueratio gr;
twohalves hh;
#else
twohalves hhfield;
#endif
#ifdef XeTeX
voidpointer ptr;
#endif
#ifdef WORDS_BIGENDIAN
integer cint;
fourquarters qqqq;
#else /* not WORDS_BIGENDIAN */
struct
{
#if defined (TeX) && !defined (SMALLTeX) || defined (MF) && !defined (SMALLMF) || defined (MP) && !defined (SMALLMP)
halfword junk;
#endif /* big {TeX,MF,MP} */
integer CINT;
} u;
struct
{
#ifndef XeTeX
#if defined (TeX) && !defined (SMALLTeX) || defined (MF) && !defined (SMALLMF) || defined (MP) && !defined (SMALLMP)
halfword junk;
#endif /* big {TeX,MF,MP} */
#endif
fourquarters QQQQ;
} v;
#endif /* not WORDS_BIGENDIAN */
} memoryword;
Once you sort through all the ifdefs and typedefs, it boils down to a union of a float (glueratio), an int32 (integer), a pair of int16s (twohalves), and a struct of 4 bytes (fourquarters). When TeX creates its memory dump file, it does it all in terms of the bytes in the fourquarters struct. (And I think there are other times that it does similar things.)This may very well be undefined behavior, but it seems to work when compiled with GCC anyway. Thankfully gc isn't such crazy code as this.
``One special guarantee is made in order to simplify the use of unions: 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 anywhere that a declaration of the complete type of the union is visible. Two structures share a common initial sequence if corresponding members have compatible types (and, for bit-fields, the same widths) for a sequence of one or more initial members.''
union hack { int x; float y};
int foo( int *i, float *f)
{
*i = 3;
*f = 42.0;
return *i;
}
Aliasing rules say foo need not read from that int pointer, may switch the order of the write to f and the write to i, and can assume that foo returns 3, so struct hack h;
foo( &h.i, &h.f);
might return 3 or something else. I think that last call introduces undefined behavior, but only becuase of the definition of foo that the writer of that call might not even have the source for.But of course, that is an "you shouldn't do that" edge case. One could also claim that the corrigendum doesn't apply because foo doesn't "use a member to access the contents of a union".
And I agree that the corrigendum doesn't apply in this case. Once you hand different-typed pointers to the same memory to people, whether via union or just casting pointers, the aliasing rules will up and bite you.
#define union struct
Here you go: #include <stdio.h>
union U { char a; char b; };
int main() {
U u;
u.a = 13;
u.b = 10;
printf("%d\n", int(u.a));
return 0;
} struct A {char a;};
struct B {char a;};
union U {struct A a; struct B b;}; typedef union
{
struct sockaddr sa;
struct sockaddr_in sin;
struct sockaddr_in6 sin6;
} addr__u;
as a way to work around the BSD socket API without horrible casting all over the place. Change the "union" to a "struct" and the code no longer works. Is it subverting the type system? Yes. Is casting subverting the type system. Yes. Pick your poison.