Notifications

No notifications

/Phase 4

Bitwise Operators

Talk to the Computer in Bits 🔢

Every int is just a row of 0s and 1s. Bitwise operators let you read, set, clear, and toggle individual bits — used in flags, permissions, low-level protocols, graphics, embedded systems, and clever algorithm tricks.

unsigned int a = 0b1100;   /* 12 */
unsigned int b = 0b1010;   /* 10 */

a & b /* AND -> 0b1000 = 8 */ a | b /* OR -> 0b1110 = 14 */ a ^ b /* XOR -> 0b0110 = 6 */ ~a /* NOT -> flip every bit */ a << 2 /* SHIFT LEFT -> 0b110000 = 48 */ a >> 1 /* SHIFT RIGHT -> 0b0110 = 6 */

The 6 Operators

OpNameWhat it does
&ANDbit is 1 only if BOTH bits are 1
\ORbit is 1 if EITHER bit is 1
^XORbit is 1 if bits DIFFER
~NOTflip every bit
<<shift leftmultiply by 2 per shift
>>shift rightdivide by 2 per shift

The 4 Bit-Tricks You'll Memorise

GoalCode
Set bit nx \= (1 << n)
Clear bit nx &= ~(1 << n)
Toggle bit nx ^= (1 << n)
Test bit n(x >> n) & 1

On this page

Detailed Theory

Bits are the atoms of computing. Once you understand bitwise operators you stop fearing flag enums, hardware registers, file permissions, and a whole class of clever algorithm tricks.

> Always use unsigned types for bit manipulation. Signed shifts and ~ on signed types can have implementation-defined or undefined behaviour. unsigned int (or uint32_t from ) is your friend.

Visualising Bits

A 1-byte (uint8_t) value 13:

bit index:  7 6 5 4 3 2 1 0
value:      0 0 0 0 1 1 0 1   = 8 + 4 + 1 = 13

Bit 0 is the LSB (least significant bit). Bit 7 is the MSB.

AND (&) — Mask / Test

1 1 0 1
& 0 1 1 1
---------
  0 1 0 1

Use AND to keep only the bits you want:

uint8_t low4 = x & 0x0F;       /* keep bottom 4 bits, zero the rest */

Test if a specific bit is on:

if (flags & FLAG_ADMIN) { ... }     /* non-zero if FLAG_ADMIN bit is set */

OR (|) — Set / Combine

1 1 0 0
| 0 0 1 1
---------
  1 1 1 1

Use OR to turn bits on:

flags 
= FLAG_READABLE
FLAG_WRITABLE; /* enable both flags */

XOR (^) — Toggle / Difference

1 1 0 0
^ 1 0 1 0
---------
  0 1 1 0

XOR flips a bit when the mask bit is 1:

x ^= (1 << 3);    /* toggle bit 3 */

XOR with itself = 0. XOR with 0 = unchanged. This gives the famous "swap without temp":

a ^= b;
b ^= a;
a ^= b;     /* now a and b are swapped */

(Cute, but use a temporary variable in real code — it's faster and clearer.)

NOT (~) — Flip Everything

uint8_t x  = 0b00001111;
uint8_t y  = ~x;          /* 0b11110000 */

Often combined with AND to clear bits:

flags &= ~FLAG_ADMIN;     /* turn off the admin flag */

Left Shift (<<) — Multiply by 2

1 << 0   ==  1
1 << 1   ==  2
1 << 2   ==  4
1 << 3   ==  8
1 << 10  == 1024

Each left shift doubles the value (bits move toward the MSB; zeros fill the right).

Right Shift (>>) — Divide by 2

16 >> 1  == 8
16 >> 2  == 4
16 >> 4  == 1

For unsigned types, zero fills from the left. For signed negatives the behaviour is implementation-defined — yet another reason to use unsigned.

The Bit-Manipulation Idioms

GoalCodeWhy
Set bit nx \= (1u << n)OR with a 1 at position n
Clear bit nx &= ~(1u << n)AND with everything except bit n
Toggle bit nx ^= (1u << n)XOR flips that bit
Test bit n(x >> n) & 1Move bit n to position 0, mask
Lowest set bitx & -xClassic isolating trick
Clear lowest set bitx & (x - 1)Used by popcount
Power of 2?x && !(x & (x - 1))Power of 2 has exactly one bit set
Even?(x & 1) == 0LSB tells parity
Multiply by 2x << 1Faster than * 2 historically
Divide unsigned by 2x >> 1Faster than / 2 historically

Flags & Permissions Pattern

This is THE most common real-world use:

enum {
    PERM_READ    = 1 << 0,    /* 0001 = 1 */
    PERM_WRITE   = 1 << 1,    /* 0010 = 2 */
    PERM_EXECUTE = 1 << 2,    /* 0100 = 4 */
    PERM_DELETE  = 1 << 3,    /* 1000 = 8 */
};

unsigned perms = 0;

perms

= PERM_READ
PERM_WRITE; /* grant read + write */ if (perms & PERM_WRITE) { ... } /* check */ perms &= ~PERM_WRITE; /* revoke write */ perms ^= PERM_EXECUTE; /* toggle execute */

This is exactly how Unix file permissions, OpenGL flags, socket options, and a million APIs work.

Counting Set Bits (popcount)

unsigned popcount(unsigned x) {
    unsigned count = 0;
    while (x) {
        x &= x - 1;     /* clears the lowest set bit */
        count++;
    }
    return count;
}

Each iteration removes exactly one set bit, so the loop runs k times for k set bits.

Hex Helps

Bits and hex line up nicely (each hex digit = 4 bits):

HexBinary
0xF1111
0xA1010
0xFF11111111
0xFF0011111111 00000000

So x & 0xFF keeps the bottom 8 bits, (x >> 8) & 0xFF extracts the next byte, etc. — useful for parsing colours, network packets, file formats.

Bitwise Cheat-Sheet

OperatorMeaningExample
&ANDflags & FLAG to test
\ORflags \= FLAG to set
^XORflags ^= FLAG to toggle
~NOT~mask to invert
<<shift left1 << n builds a bitmask
>>shift right(x >> n) & 1 extracts a bit

Bitwise operators feel like cheat codes once you internalise them — and you'll see them everywhere from competitive programming to embedded firmware.