5 ms·
> #define union struct /* keeps code correct, just wastes some space */ Not always, though...
by piokuc 12y ago
> #define union struct /* keeps code correct, just wastes some space */
Not always, though...
- rsc 12y agoYes, always. And if you don't believe me, it's not my trick. I learned it from Dennis Ritchie (he was thinking about a C to Limbo converter).
- andybalholm 12y agoIt works when unions are used correctly. If they are used to deliberately subvert the type system (e.g., put in an int, get an array of char out) it doesn't work. But C has so many other ways to subvert the type system that there's no need to do that. 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.
- piokuc 12y agoThat's why I said "not always". Here is an example which will print different result if you define #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; }
- to3m 12y agoI don't think a and b need necessarily overlap. I think the types need to be more like this: struct A {char a;}; struct B {char a;}; union U {struct A a; struct B b;};
- rsc 12y agoThat code is not valid according to the C standard, so there is no guarantee it will work anywhere. In particular, many modern compilers have optimizations that would break that code. I would be a little surprised if modern web2c still uses unions this way and gets away with it. 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);
- andybalholm 12y agoI finally managed to track down the definition of memoryword from texlive-20130530-source/texk/web2c/texmfmem.h. Here it is: 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.
- to3m 12y agoC99 6.5.2.3.5 seems to suggest that non-overlapping union members could break conforming code: ``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.''
- bzbarsky 12y agoWhich part of the C standard would forbid type punning a uint32 to a float32? http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_283.htm http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_283.htm suggests that C89 explicitly allowed this, C99 at first glance did not, and was errata'ed to make it clear that this is still allowed. C11 seems to have the same verbiage.
- spc476 12y agoIt would break my current usage of unions. 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.