Unions and Bit-Fields
How a union overlays its members, what reading the wrong member means, and bit-fields for packed flags.
One Space, Several Costumes
A struct gives every member its own storage; a union gives all its members the same storage, overlaid on one spot in memory. It is the struct's stranger sibling, sized by its largest member, holding exactly one meaningful value at a time, and it appears on every syllabus alongside bit-fields, the struct feature that packs members into individual bits. Both are specialist tools; this lesson gives you their exact semantics and the honest scope of their use.
The Union's Deal
Declaration syntax mirrors struct exactly, tag rule included: the type is union Amount, both words. The behavior differs in one sentence: all members start at the same address, so sizeof reports (at least) the largest member, 8 here for the double, not the 16 a struct with the same members needs on this platform (last lesson's padding rule at work), and writing one member overwrites them all.
The consequence with teeth: after box.weight = 2.75, the earlier count is gone, and reading a member other than the one last written reinterprets the stored bytes as a different type; the value is at best meaningless and the practice is the union's cardinal sin. The rule to carry: a union holds one value; you must remember which.
Initializing a Union
A brace initializer on a union always targets the first member, and C89 offers no syntax for reaching any other one:
union Amount box = {12}; /* count = 12, the first member */
union Amount odd = {2.75}; /* still count: 2.75 converts to int, so count is 2 */
The second line is the trap. It reads as though it initializes weight and it does not: the value is converted to the first member's type exactly as int count = 2.75; would convert it, leaving odd.count as 2 and weight never written. Exam papers state the rule in the stronger form, "a union may be initialized only with a value of its first member's type", and answering that way is safe; what a compiler actually does with a mismatched arithmetic type is convert it rather than reject it. Every other member gets its value the ordinary way, by assignment after the declaration. (C99 added designated initializers, = {.weight = 2.75}, which do name a chosen member; C89 has no equivalent.)
The Tagged Union: The Pattern That Makes It Safe
"Remember which" is a job for a struct wrapping the union with a tag, an int (or enum-style constant set) recording the active member:
Every read consults the tag first, so the wrong-member sin becomes structurally impossible. This tagged union is the pattern behind every "value that can be one of several types" in C, and the exam question "how do you use a union safely?" has exactly this answer: pair it with a discriminating tag and switch on the tag before touching a member.
When is the memory saving worth the ceremony? Large arrays of records where each holds one of several bulky alternatives; message and packet formats where a header's type field plays the tag; memory-constrained embedded work. In everyday code, two separate members in a struct cost little and confuse nobody, which is why unions are a specialist tool.
Bit-Fields: Members Measured in Bits
A struct member can declare a width in bits:
isActive : 1 occupies one bit; level : 4 holds 0 through 15; the three members pack into a single unsigned int's space instead of twelve bytes. Access reads like any member, with the compiler doing the masking and shifting that chapter 3's bitwise operators would spell by hand, and a value too wide for its field is simply truncated to the low bits: level = 20 stores 4.
The fine print exams probe: bit-field members should be declared unsigned int in this dialect (plain int fields are implementation-defined in signedness); a bit-field has no address, so &user.level does not compile and scanf can never fill one directly; and the packing order and padding are implementation-defined, so bit-fields describe your program's flags well but portable file and network formats use explicit bitwise operators instead.
Three more limits round the feature out. Bit-fields cannot be arrayed: unsigned int flags[4] : 1; is rejected outright, gcc calling it bit-field 'flags' has invalid type, because a width describes one member and an array of widths means nothing; four separate one-bit members is how that gets written. An unnamed field reserves bits that no code can name, unsigned int : 2; stepping over two of them, which is how a layout leaves a deliberate hole. And what happens to a field too wide for the space left in the storage unit is implementation-defined: on this platform it does not straddle the boundary, starting in the next unit and abandoning the leftover bits. Which is the same lesson a third time, rely on the values, never on the layout.
Key Takeaways
- A union overlays all members at one address: type name
union Tag, size at least its largest member, exactly one meaningful value at a time. - Reading a member other than the last one written reinterprets bytes; the tagged-union pattern (a struct pairing a tag with the union) is the safe idiom and the exam answer.
- Unions earn their keep in bulk storage, message formats, and embedded memory pressure; otherwise plain structs read better.
- A union's brace initializer always targets the first member, converting the value to that member's type; other members are set by assignment afterwards.
- Bit-fields (
unsigned int flag : 1) pack members into bits with compiler-managed masking; oversized stores truncate. - Bit-fields have no address and implementation-defined layout: fine for internal flags, wrong for portable formats, where explicit bitwise code rules.
- Bit-fields cannot be arrayed, unnamed fields (
unsigned int : 2;) reserve unreachable bits, and a field that will not fit moves to the next storage unit rather than straddling it.
How did you find this lesson?
Your rating helps us improve the content.
Create an account to track your progress and access interactive exercises. Already have one? Sign in.
Unions and Bit-Fields - Quiz
Test your understanding of the lesson.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!