Bitwise Operators and Bitmasking
Control Flow and Bit Manipulation

2.3 Bitwise Operators and Bitmasking

I want to take you down to the binary level where memory is scarce. In high level languages, programmers use bool variables to represent true/false states. In C++, a single bool consumes a full byte (8 bits) of memory because the CPU cannot address individual bits directly.

If you have eight boolean flags in your game engine or network package, storing them as separate booleans wastes 64 bits of space. Using bitwise operations, we can pack all eight states into a single byte, saving memory bandwidth and cache space.

The Bitwise Operators

Bitwise operators manipulate the binary bits of variables directly:

* AND (&): Returns 1 if both bits are 1.

* OR (|): Returns 1 if at least one bit is 1.

* XOR (^): Returns 1 if the bits are different.

* NOT (~): Flips all bits (0 becomes 1, 1 becomes 0).

* Left Shift (<<): Shifts bits to the left, padding with zeros.

* Right Shift (>>): Shifts bits to the right.

Bitmasking: Packing State Flags

We can assign specific bit positions to represent different states in our application. These are called bit masks. Let's see how we can pack, set, clear, and check flags inside a single byte:

#include <iostream>

// Define masks using bit shifts
constexpr unsigned char flagActive{1 << 0};  // Binary: 00000001
constexpr unsigned char flagVisible{1 << 1}; // Binary: 00000010
constexpr unsigned char flagDirty{1 << 2};   // Binary: 00000100
constexpr unsigned char flagMuted{1 << 3};   // Binary: 00001000

int main() {
    unsigned char playerState{0}; // Start with all flags off (00000000)

    // 1. Set flags (turn ON using OR operator)
    playerState |= flagActive;  // playerState is now 00000001
    playerState |= flagVisible; // playerState is now 00000011

    // 2. Check flags (using AND operator)
    if (playerState & flagVisible) {
        std::cout << "Player is visible!\n";
    }

    // 3. Clear flags (turn OFF using AND with inverted NOT mask)
    playerState &= ~flagVisible; // playerState is now 00000001 (turns Visible off)

    // 4. Toggle flags (using XOR operator)
    playerState ^= flagMuted; // playerState is now 00001001 (turns Muted on)
    playerState ^= flagMuted; // playerState is now 00000001 (turns Muted off again)

    return 0;
}
Managing compact state flags with bitwise operators.

Compacting Your Memory footprint

Bitmasking is like folding your clothes tightly before packing a suitcase. Instead of throwing each shirt in loose (wasting entire sections of cache memory), you fold them into tiny, specific coordinates. In high performance rendering pipelines and networking protocols, bitmasking is the standard way to transmit packets and manage options efficiently.

Finished reading this lesson?