Exercises — Bit fields in structs
Parent: Bit fields in structs. This page is a self-testing ladder. Each problem states the question cleanly; the Solution is hidden inside a collapsible callout so you can try first, then reveal. Levels climb from recognising the syntax to mastering real hardware-register layouts.
Before we start, one shared picture to lean on. A bit field lives inside a storage unit — think of an unsigned int as a row of numbered slots, filled by fields in these figures. Keep this in mind for every packing question.

Recall the vault prerequisites if any word feels new: Data types and sizeof, Bitwise operators, Unsigned vs signed integers, Memory alignment and padding, Structs in C.
Level 1 — Recognition
Recall Solution — L1·Q1
Answer: (b).
- (a) illegal — the
typeof a bit field must be one of the portable integer types permitted by the standard (signed int,unsigned int,_Bool).floatis not. - (c) illegal — the width must be a non-negative integer constant;
2.5is not an integer. - (d) illegal — a bit field is a single scalar, you cannot make an array a bit field.
- (b) legal —
type member : width;with an allowed integer type and an integer width. ✓
Recall Solution — L1·Q2
Use the counting rule: bits give combinations, largest is all-ones . Largest = 15.
Level 2 — Application
Recall Solution — L2·Q1
Why this step: we need , i.e. .
- ✓
Answer: bits. Declaration: unsigned int val : 10; (holds ).
Recall Solution — L2·Q2
A 3-bit field keeps only the low 3 bits = value mod .
Prints 4. No error, no warning — silent truncation. Look at figure s02: the top bits of 20 simply fall off the left edge of the 3-slot box.

Recall Solution — L2·Q3
A signed field spends its top bit on the sign (see Unsigned vs signed integers). With bits in two's complement: Answer: −8 to 7 (16 values total, but only 0..7 are non-negative).
Level 3 — Analysis
Recall Solution — L3·Q1
Track the offset in one 32-bit unit (figure s03):
atakes bits 0–4 (offset now 5).btakes bits 5–9 (offset now 10).cneeds 25 bits, but only remain → won't fit. It starts a fresh unit (bits 0–24 of unit #2).
Two storage units are used → bytes.
sizeof(struct S) = 8 under these assumptions (32-bit unit, left-to-right packing). On a compiler with a different unit size or packing direction the number can differ — the reasoning, not the constant, is the point.

Recall Solution — L3·Q2
unsigned : 0; is the align-to-next-unit instruction: it forces the field after it to begin a fresh storage unit.
low→ bits 0–3 of unit #1.: 0→ close unit #1, jump to unit #2.high→ bits 0–3 of unit #2.
Two units → sizeof(struct R) = 8 bytes (assuming 4-byte units). (Contrast: without the : 0 divider, both 4-bit fields fit in one unit → 4 bytes.) See Memory alignment and padding.
Recall Solution — L3·Q3
It does NOT compile. The smallest thing a C pointer can address is a byte. A 1-bit field may sit in the middle of a byte (e.g. bit 3 of byte 0), which has no byte address of its own. So &s.flag is a compile error.
Fix — copy to a normal variable first:
unsigned t = s.flag; // t is a full addressable variable
unsigned *p = &t; // legalLevel 4 — Synthesis
Recall Solution — L4·Q1
Sum the widths: bits → the logical layout needs exactly one byte. In portable standard C the only allowed bit-field types are unsigned int / signed int / _Bool, so we declare with unsigned int (see Embedded systems / hardware registers):
struct StatusReg {
unsigned int ready : 1; // bit 0
unsigned int speed : 3; // bits 1..3
unsigned int error : 2; // bits 4..5
unsigned int reserved : 2; // bits 6..7
};All 8 logical bits are accounted for; speed correctly holds . ✓
Honest caveat about sizeof: because the storage unit is an unsigned int, sizeof(struct StatusReg) is typically 4 bytes, not 1 — the fields live in the low 8 bits of a 4-byte unit, the rest is padding. You cannot portably force a 1-byte struct: unsigned char ready : 1; is a compiler extension (GCC/Clang accept it, but the standard does not list char as a bit-field type, and even then it may still pack into an int on some targets). If you truly need an exact 1-byte hardware image, the portable route is a raw unsigned char plus manual shift-and-mask (next exercise), or a compiler-specific __attribute__((packed)).
Recall Solution — L4·Q2
speed sits at bits 1–3. Shift it down so bit 1 lands at position 0, then mask to keep 3 bits.
- Shift right by 1:
reg >> 1brings bits 1–3 to positions 0–2. - Mask with :
& 0x7keeps exactly those 3 bits.
unsigned speed = (reg >> 1) & 0x7;Check with reg = 0b01011010 (= 90): reg >> 1 = 0b00101101, & 0b111 = 0b101 = 5. So speed = 5. This shift-and-mask is fully portable — it does not depend on packing direction, unit size, or signedness the way a bit-field layout does.
Level 5 — Mastery
Recall Solution — L5·Q1
Each field is 4 bits → keep value mod .
s.a = 25: . (Binary: , low 4 bits .)s.b = 7: , stored as-is .
Output: 9 7. Both fields live in the same storage unit (4+4 = 8 bits ≤ 32), independent of each other. (The values 9 and 7 are portable; which physical bits hold them depends on packing direction.)
Recall Solution — L5·Q2
Place each field at its starting bit and add (this is packing, done by hand — order fixed by the problem, so it's unambiguous):
ready(bit 0):speed(bits 1–3):error(bits 4–5):reserved(bits 6–7):
Answer: 43. Reading right-to-left: reserved=00, error=10, speed=101, ready=1 — matches the inputs. ✓
Recall Solution — L5·Q3
p→ bits 0–9 of unit #1 (offset 10).q→ bits 10–19 of unit #1 (offset 20).: 0→ close unit #1, force next field to a fresh unit.r→ bits 0–9 of unit #2.
Even though r (10 bits) would have fit in unit #1's remaining 12 bits, the zero-width field pushed it to unit #2. Two units used → sizeof(struct M) = 8 bytes (under the 32-bit-unit assumption).
Recall Rapid recap — the four moves you practised
Truncation ::: value stored = assigned value mod (keep low N bits).
Signed range ::: to ; unsigned range to .
Packing rule ::: fill a storage unit until a field won't fit; then start a fresh unit.
Extract a field at bit k ::: (reg >> k) & mask — shift first, then mask.
Which parts are portable ::: values/widths/wrapping are; exact byte layout (packing order, unit size) is implementation-defined.
Connections
- Parent: Bit fields in structs
- Bitwise operators — the manual shift-and-mask behind every field.
- Data types and sizeof — why the unit is an
intandsizeofjumps in chunks. - Memory alignment and padding — the zero-width divider in action.
- Embedded systems / hardware registers — the L4/L5 status-register pattern.
- Unsigned vs signed integers — the lost sign bit in L2·Q3.
- Structs in C — bit fields are just special struct members.