CppBitwise Operators

Bitwise Operators

Bitwise operators work directly on the individual binary bits of an integer's representation, rather than treating it as a single numeric value. They are essential for flags, masks, low-level protocol work, and certain performance optimizations.

The Operators

Operator

Name

Behavior

&

AND

Bit is 1 only if both corresponding bits are 1

|

OR

Bit is 1 if at least one corresponding bit is 1

^

XOR

Bit is 1 if exactly one corresponding bit is 1

~

NOT (complement)

Flips every bit (0 becomes 1, 1 becomes 0)

<<

Left shift

Shifts bits left, filling with 0s (multiplies by 2 per shift)

Right shift

Shifts bits right (divides by 2 per shift for unsigned/positive values)

Binary Representation
An int is stored as a sequence of bits. For example, the number{' '} 5 in an 8-bit view is 00000101. Bitwise operators act on these individual 0s and 1s directly.

CPP
#include <iostream>
#include <bitset>

int main() {
    int a = 5;  // 00000101
    int b = 3;  // 00000011

    std::cout << std::bitset<8>(a & b) << std::endl;  // 00000001 -> 1
    std::cout << std::bitset<8>(a | b) << std::endl;  // 00000111 -> 7
    std::cout << std::bitset<8>(a ^ b) << std::endl;  // 00000110 -> 6
    std::cout << std::bitset<8>(~a) << std::endl;     // 11111010 (all bits flipped)
    std::cout << (a << 1) << std::endl;               // 10 (5 * 2)
    std::cout << (a >> 1) << std::endl;                // 2 (5 / 2, truncated)
    return 0;
}
Practical Use Cases
  • Bitmasks/flags — pack many independent boolean options into a single integer

  • Fast multiply/divide by powers of twox << 1 doubles x, x >> 1 halves it (for non-negative integers)

  • Toggling and testing individual bits — hardware registers, compact state representations, checksum/hash algorithms

Worked Example: Bit Flags for Settings

CPP
#include <iostream>
#include <cstdint>

// Each flag occupies one distinct bit, so they can be combined with |
constexpr uint8_t FLAG_VISIBLE  = 1 << 0; // 0001
constexpr uint8_t FLAG_ENABLED  = 1 << 1; // 0010
constexpr uint8_t FLAG_SELECTED = 1 << 2; // 0100

int main() {
    uint8_t settings = 0;

    settings |= FLAG_VISIBLE;             // turn a flag on
    settings |= FLAG_ENABLED;

    bool isVisible = settings & FLAG_VISIBLE;  // test a flag
    std::cout << "Visible: " << isVisible << std::endl;

    settings &= ~FLAG_ENABLED;            // turn a flag off (AND with the complement)
    bool isEnabled = settings & FLAG_ENABLED;
    std::cout << "Enabled: " << isEnabled << std::endl;

    settings ^= FLAG_SELECTED;            // toggle a flag
    return 0;
}
Prefer std::bitset for readability
Raw integer bit flags are compact and fast but easy to misread. For many programs, std::bitset<N> from <bitset> is a clearer, safer alternative — it gives you named bit access, bounds-checked indexing, and readable printing, while still being just as efficient under the hood.

CPP
#include <bitset>
#include <iostream>

int main() {
    std::bitset<8> settings; // all bits start at 0

    settings.set(0);          // turn bit 0 on (like FLAG_VISIBLE)
    settings.set(1);          // turn bit 1 on (like FLAG_ENABLED)

    std::cout << settings << std::endl;      // 00000011
    std::cout << settings.test(0) << std::endl; // true
    settings.reset(1);                        // turn bit 1 off
    settings.flip(2);                         // toggle bit 2
    return 0;
}
Note
Right-shifting a negative signed integer (>>) is implementation-defined behavior in older standards (arithmetic vs logical shift) and was only fully standardized as arithmetic shift in C++20. Avoid shifting negative values unless you have confirmed your target standard and compiler behavior.