CodeNebula
C++98corebeginner

Bitfields

C++98 · The Foundation · Compiler flag: -std=c++98

Explanation

Struct members with exact bit widths, enabling compact storage of flags and small values.

Real-World Scenario

Pack hardware register flags into a single byte in a device driver.

Quick Snippet

snippet.cpp
struct Flags { unsigned int bold : 1; unsigned int italic : 1; };

Full Example

bitfields.cpp
// CodeNebula - C++98 Example: Bitfields
// Compile: g++ -std=c++98 -Wall -Wextra -o example bitfields.cpp

// Demonstrates bitfield members in structs: packing multiple values into
// minimal bits, flag fields, practical use cases, and size comparisons.

#include <iostream>
#include <cstring>

// ------------------------------------------------------------------
// 1. Basic bitfield struct - packs flags into a single byte (or few bytes)
// ------------------------------------------------------------------
struct FilePermissions {
    unsigned int read    : 1;   // 1 bit: 0 or 1
    unsigned int write   : 1;   // 1 bit: 0 or 1
    unsigned int execute : 1;   // 1 bit: 0 or 1
    unsigned int hidden  : 1;   // 1 bit: 0 or 1
};

// ------------------------------------------------------------------
// 2. Bitfield with multi-bit fields
// ------------------------------------------------------------------
struct DatePacked {
    unsigned int day   : 5;     // 1-31  (needs 5 bits)
    unsigned int month : 4;     // 1-12  (needs 4 bits)
    unsigned int year  : 12;    // 0-4095 (needs 12 bits)
};

// Equivalent without bitfields for size comparison
struct DateNormal {
    unsigned int day;
    unsigned int month;
    unsigned int year;
};

// ------------------------------------------------------------------
// 3. Bitfield for a color value (RGB565 format, common in embedded)
// ------------------------------------------------------------------
struct RGB565 {
    unsigned short red   : 5;   // 0-31
    unsigned short green : 6;   // 0-63
    unsigned short blue  : 5;   // 0-31
};

// ------------------------------------------------------------------
// 4. Bitfield with signed fields
// ------------------------------------------------------------------
struct SmallInts {
    signed int a : 4;    // -8 to 7
    signed int b : 4;    // -8 to 7
    signed int c : 8;    // -128 to 127
};

// ------------------------------------------------------------------
// 5. TCP-like header flags (practical networking example)
// ------------------------------------------------------------------
struct TcpFlags {
    unsigned int fin : 1;
    unsigned int syn : 1;
    unsigned int rst : 1;
    unsigned int psh : 1;
    unsigned int ack : 1;
    unsigned int urg : 1;
    unsigned int reserved : 2;  // padding / reserved bits
};

void printPermissions(const FilePermissions& p)
{
    std::cout << (p.read    ? "r" : "-")
              << (p.write   ? "w" : "-")
              << (p.execute ? "x" : "-")
              << (p.hidden  ? "h" : "-");
}

int main()
{
    // ------------------------------------------------------------------
    // Demo 1: File permission flags
    // ------------------------------------------------------------------
    std::cout << "=== File Permission Bitfields ===" << std::endl;

    FilePermissions fp;
    std::memset(&fp, 0, sizeof(fp));    // zero all bits

    fp.read = 1;
    fp.write = 1;
    fp.execute = 0;
    fp.hidden = 0;

    std::cout << "  Permissions: ";
    printPermissions(fp);
    std::cout << std::endl;
    std::cout << "  sizeof(FilePermissions): " << sizeof(FilePermissions)
              << " byte(s) (4 flags in ~1 byte, but may be padded to int)"
              << std::endl;
    std::cout << std::endl;

    // Modify individual flags
    fp.execute = 1;
    fp.hidden = 1;
    std::cout << "  After adding execute and hidden: ";
    printPermissions(fp);
    std::cout << std::endl;
    std::cout << std::endl;

    // ------------------------------------------------------------------
    // Demo 2: Packed date vs. normal struct
    // ------------------------------------------------------------------
    std::cout << "=== Packed Date ===" << std::endl;

    DatePacked dp;
    std::memset(&dp, 0, sizeof(dp));
    dp.day   = 22;
    dp.month = 3;
    dp.year  = 2026;

    std::cout << "  Date: " << dp.year << "-"
              << dp.month << "-" << dp.day << std::endl;
    std::cout << "  sizeof(DatePacked)  : " << sizeof(DatePacked)
              << " byte(s)" << std::endl;
    std::cout << "  sizeof(DateNormal)  : " << sizeof(DateNormal)
              << " byte(s)" << std::endl;
    std::cout << "  Space saved: " << sizeof(DateNormal) - sizeof(DatePacked)
              << " byte(s)" << std::endl;
    std::cout << std::endl;

    // ------------------------------------------------------------------
    // Demo 3: RGB565 color packing
    // ------------------------------------------------------------------
    std::cout << "=== RGB565 Color ===" << std::endl;

    RGB565 color;
    std::memset(&color, 0, sizeof(color));
    color.red   = 31;   // max red
    color.green = 0;
    color.blue  = 0;

    std::cout << "  Pure red:   R=" << color.red
              << " G=" << color.green
              << " B=" << color.blue << std::endl;

    color.red   = 0;
    color.green = 63;   // max green
    color.blue  = 0;
    std::cout << "  Pure green: R=" << color.red
              << " G=" << color.green
              << " B=" << color.blue << std::endl;

    color.red   = 31;
    color.green = 63;
    color.blue  = 31;
    std::cout << "  White:      R=" << color.red
              << " G=" << color.green
              << " B=" << color.blue << std::endl;

    std::cout << "  sizeof(RGB565): " << sizeof(RGB565)
              << " byte(s) (16 bits packed into 2 bytes)" << std::endl;
    std::cout << std::endl;

    // ------------------------------------------------------------------
    // Demo 4: Signed bitfields
    // ------------------------------------------------------------------
    std::cout << "=== Signed Bitfields ===" << std::endl;

    SmallInts si;
    std::memset(&si, 0, sizeof(si));
    si.a = 5;
    si.b = -3;
    si.c = -100;

    std::cout << "  a (4-bit signed) = " << si.a << "  (range: -8 to 7)" << std::endl;
    std::cout << "  b (4-bit signed) = " << si.b << "  (range: -8 to 7)" << std::endl;
    std::cout << "  c (8-bit signed) = " << si.c << "  (range: -128 to 127)" << std::endl;

    // Show overflow behavior: 4-bit signed can only hold -8..7
    si.a = 7;
    std::cout << "  a set to 7       = " << si.a << "  (max for 4-bit signed)" << std::endl;
    std::cout << "  sizeof(SmallInts): " << sizeof(SmallInts) << " byte(s)" << std::endl;
    std::cout << std::endl;

    // ------------------------------------------------------------------
    // Demo 5: TCP flags
    // ------------------------------------------------------------------
    std::cout << "=== TCP-like Flags ===" << std::endl;

    TcpFlags tcp;
    std::memset(&tcp, 0, sizeof(tcp));

    // SYN packet
    tcp.syn = 1;
    std::cout << "  SYN packet:     "
              << "FIN=" << tcp.fin << " SYN=" << tcp.syn
              << " RST=" << tcp.rst << " PSH=" << tcp.psh
              << " ACK=" << tcp.ack << " URG=" << tcp.urg << std::endl;

    // SYN-ACK packet
    tcp.ack = 1;
    std::cout << "  SYN-ACK packet: "
              << "FIN=" << tcp.fin << " SYN=" << tcp.syn
              << " RST=" << tcp.rst << " PSH=" << tcp.psh
              << " ACK=" << tcp.ack << " URG=" << tcp.urg << std::endl;

    // ACK + PSH (data packet)
    tcp.syn = 0;
    tcp.psh = 1;
    std::cout << "  Data packet:    "
              << "FIN=" << tcp.fin << " SYN=" << tcp.syn
              << " RST=" << tcp.rst << " PSH=" << tcp.psh
              << " ACK=" << tcp.ack << " URG=" << tcp.urg << std::endl;

    std::cout << "  sizeof(TcpFlags): " << sizeof(TcpFlags) << " byte(s)" << std::endl;

    return 0;
}

/*
Expected output:

=== File Permission Bitfields ===
  Permissions: rw--
  sizeof(FilePermissions): 4 byte(s) (4 flags in ~1 byte, but may be padded to int)

  After adding execute and hidden: rwxh

=== Packed Date ===
  Date: 2026-3-22
  sizeof(DatePacked)  : 4 byte(s)
  sizeof(DateNormal)  : 12 byte(s)
  Space saved: 8 byte(s)

=== RGB565 Color ===
  Pure red:   R=31 G=0 B=0
  Pure green: R=0 G=63 B=0
  White:      R=31 G=63 B=31
  sizeof(RGB565): 2 byte(s) (16 bits packed into 2 bytes)

=== Signed Bitfields ===
  a (4-bit signed) = 5  (range: -8 to 7)
  b (4-bit signed) = -3  (range: -8 to 7)
  c (8-bit signed) = -100  (range: -128 to 127)
  a set to 7       = 7  (max for 4-bit signed)
  sizeof(SmallInts): 4 byte(s)

=== TCP-like Flags ===
  SYN packet:     FIN=0 SYN=1 RST=0 PSH=0 ACK=0 URG=0
  SYN-ACK packet: FIN=0 SYN=1 RST=0 PSH=0 ACK=1 URG=0
  Data packet:    FIN=0 SYN=0 RST=0 PSH=1 ACK=1 URG=0
  sizeof(TcpFlags): 4 byte(s)
*/
Run command
g++ -std=c++98 -Wall -Wextra -o example bitfields.cpp