CBit Fields

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

C
#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
Bit field layout is implementation-defined
The C standard leaves the exact memory layout of bit fields — whether the first declared field occupies the most or least significant bits, how fields are packed across byte boundaries, and even the overall struct size — up to the compiler. Code that relies on a specific bit field layout (for example, to match an exact binary file format or network protocol byte-for-byte) can behave differently, or break outright, when compiled with a different compiler, a different compiler version, or for a different target architecture.

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 rule of thumb
Reach for bit fields when you want convenient, self-documenting struct-like access to a set of small values within a single program, on a single target. Reach for manual bitmasks when the exact bit layout must be guaranteed and portable.
  • 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.