Bit-Fields
This chapter covers bit-fields: struct members that occupy a specified number of bits rather than whole bytes. They are used for hardware registers, packed protocol fields, and flags, but their layout is highly implementation-defined.
Why This Matters
Bit-fields are the convenient, self-documenting way to pack small values into an integer-sized unit. However, their bit ordering and padding are implementation-defined, which makes them dangerous for portable binary formats or cross-compiler hardware access.
Prerequisites
c.core.26— structs, padding, alignment.
Core Concept
A bit-field is a struct member declared with a width:
struct Flags {
unsigned int a : 1;
unsigned int b : 3;
unsigned int c : 4;
};
Each member occupies the specified number of bits. The struct packs them into an addressable storage unit (typically an unsigned int), with layout determined by the implementation.
Bit-fields are normally declared as unsigned int, signed int, or _Bool (and, in C99, other implementation-defined types). Using plain int is allowed but implementation-defined in signedness.
Syntax
struct Name {
type member : width;
};
The width must be a non-negative integer constant expression and cannot exceed the width of the declared type.
Examples
Basic flags
#include <stdio.h>
struct Flags {
unsigned int ready : 1;
unsigned int error : 1;
unsigned int mode : 2;
};
int main(void)
{
struct Flags f = {0};
f.ready = 1;
f.mode = 2;
printf("ready=%u error=%u mode=%u\n", f.ready, f.error, f.mode);
return 0;
}
Expected output: ready=1 error=0 mode=2.
How It Works
The compiler allocates one or more addressable storage units (typically unsigned int) and packs bit-fields into them. The order of allocation (low bit first vs. high bit first), whether fields can straddle unit boundaries, and the padding between them are all implementation-defined.
Variations
Bit-fields vs. manual masking
You can achieve the same effect with shifts and masks (c.emb.mask), which is fully portable but more verbose. Bit-fields are more readable but less portable in layout.
Unnamed and zero-width bit-fields
- An unnamed bit-field with width 0 forces alignment to the next storage unit.
- An unnamed bit-field with a nonzero width creates padding.
struct S {
unsigned int a : 1;
unsigned int : 0; /* force next field to new unit */
unsigned int b : 1;
};
Common Mistakes
- Assuming bit order is portable (it is not).
- Assuming bit-fields are packed without padding (padding exists).
- Using bit-fields for on-disk or network formats where portability matters.
- Accessing a bit-field through a pointer of a different type.
Undefined Behavior
- Taking the address of a bit-field is not allowed (bit-fields may not be
aligned at byte boundaries and are not objects in the usual sense). VERIFIED
- Accessing a bit-field through a pointer to the struct is fine; accessing the
underlying storage in a way that violates aliasing is UB.
Portability
- Bit-field layout (order, padding, straddling) is implementation-defined.
VERIFIED
- The signedness of a plain
intbit-field is implementation-defined; use
signed int or unsigned int explicitly.
- Bit-fields are not suitable for portable binary data exchange; use explicit
shift/mask for that.
Under the Hood
The compiler generates shift-and-mask sequences for bit-field access: read the storage unit, mask out the field, shift to position, and write back. This is why bit-field access can be slightly more expensive than a plain variable access but far more readable than manual bit manipulation.
Practical Usage
- Use bit-fields for in-memory flags and configuration words where portability
of layout is not required.
- Use explicit masks/shifts for hardware registers and wire formats that must
be portable and bit-exact.
Exercises
1. Define a struct with bit-fields of various widths and print sizeof and each field's value. 2. Compare a bit-field struct with an equivalent manual mask/shift implementation. 3. Demonstrate that you cannot take the address of a bit-field (observe the compiler error). 4. Use a zero-width bit-field to force alignment and observe the effect on sizeof.
Deep Challenge
Explain, using the C standard's rules, why bit-fields are unsafe for reading a hardware register on a different compiler or architecture, and write a portable mask/shift alternative for a 32-bit register with a 4-bit field and a 1-bit flag. Discuss the trade-offs.
Related Concepts
c.emb.mask/c.emb.shift— portable bit manipulation.c.ops.bitwise— bitwise operators.c.struct.padding— struct layout.
References
- ISO/IEC 9899:2018 §6.7.2.1 (bit-fields).
Verification
- Bit-field layout is implementation-defined.
VERIFIED - Address-of a bit-field is not allowed.
VERIFIED - Zero-width bit-field forces a new unit.
VERIFIED - No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] Bit-field declaration
- [ ] Layout portability
- [ ] Unnamed and zero-width bit-fields
- [ ] Bit-fields vs. masks
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.struct.bitfield | 0 | 6 |