Bit Fields
A normal struct member gets a whole number of bytes, even if the value it holds only ever needs a couple of bits (a boolean flag, a 3-bit priority level). A bit field lets you tell the compiler exactly how many bits a member should occupy, packing several small values into a single byte or word instead of wasting a whole byte on each.
Declaring bit-width struct members
#include <stdio.h>
struct Flags {
unsigned int is_active : 1; // occupies 1 bit
unsigned int is_visible : 1; // occupies 1 bit
unsigned int priority : 3; // occupies 3 bits (values 0-7)
unsigned int reserved : 3; // occupies 3 bits, unused for now
};
int main(void) {
struct Flags f = {0};
f.is_active = 1;
f.is_visible = 1;
f.priority = 5;
printf("active=%u visible=%u priority=%u\n",
f.is_active, f.is_visible, f.priority);
printf("sizeof(struct Flags) = %zu bytes\n", sizeof(struct Flags));
return 0;
}active=1 visible=1 priority=5 sizeof(struct Flags) = 4 bytes
All four members above add up to only 8 bits, but the struct still reports 4 bytes on many platforms — the compiler is free to choose how it packs and pads bit fields, which is part of why they are less predictable than they first appear.
Where bit fields are used
Bit fields are most common in embedded and systems programming, where a struct is used to describe a hardware register or a protocol header whose individual bits each carry separate meaning — for example, a status register where bit 0 is a ready flag, bit 1 is an error flag, and bits 2-4 encode a mode. Writing a bit field struct that mirrors the register layout can make that code much more readable than manual shifting and masking scattered everywhere.
The portability problem
When you need a guaranteed, portable binary layout — matching a file format, a network protocol, or hardware documentation exactly — explicit bitmasks and shift operations over a plain, fixed-width integer type give you full control over every bit, with none of the compiler-dependent guesswork. See Bitmasks & Flags for that approach.
A bit field member declares its width in bits:
unsigned int name : width;.Bit fields pack several small values compactly instead of giving each its own byte.
Common in embedded code that models hardware registers or compact flag sets.
The exact bit ordering, packing, and resulting struct size are implementation-defined and not portable across compilers.