Skip to content
FollowtheDamnCode

Bitwise Operators

Working on the 0s and 1s inside a number.

7 minute read

Big idea

Every integer is stored as bits — 0s and 1s. Bitwise operators work on those bits directly: &, |, ^, ~, <<, >>.

Binary in one minute

We count in tens: 13 means 1 ten + 3 ones. Computers count in twos. Each bit position is worth double the one to its right:

Bit value8421
13 in binary1101

8 + 4 + 1 = 13, so 13 is 1101 in binary. You can write binary directly in C with 0b (newer compilers) or, more traditionally, hex with 0x: 0xF is 1111.

The six operators

Compare two numbers bit by bit, column by column:

OperatorNameA result bit is 1 when…12 op 10
&ANDboth bits are 11100 & 1010 = 1000 = 8
|OReither bit is 11100 | 1010 = 1110 = 14
^XORthe bits differ1100 ^ 1010 = 0110 = 6
~NOT(one number) flips every bit
<<left shift(moves bits left, filling with 0s)3 << 2 = 1100 = 12
>>right shift(moves bits right)12 >> 2 = 0011 = 3
Bitwise operators on 12 and 10
#include <stdio.h>

int main(void) {
    int a = 12, b = 10;   // 1100 and 1010
    printf("a & b  = %d\n", a & b);
    printf("a | b  = %d\n", a | b);
    printf("a ^ b  = %d\n", a ^ b);
    printf("a << 1 = %d\n", a << 1);
    printf("a >> 2 = %d\n", a >> 2);
    return 0;
}
output
a & b  = 8
a | b  = 14
a ^ b  = 6
a << 1 = 24
a >> 2 = 3

Run and edit it in the app.

Notice: shifting left by 1 doubles a number; shifting right by 1 halves it (dropping any remainder).

Don't mix them up with && and ||

&& and || are logical: they treat the whole number as true/false and give 1 or 0. & and | work on each bit:

Predict: logical vs bitwise
#include <stdio.h>

int main(void) {
    int a = 4, b = 2;     // 100 and 010
    printf("%d %d\n", a && b, a & b);
    printf("%d %d\n", a || b, a | b);
    return 0;
}
What does it print? Show the answer
1 0
1 6

Run and edit it in the app.

Both 4 and 2 are "true", so a && b is 1. But they share no 1-bits, so a & b is 0.

Using bits as on/off switches

A single int can hold 32 yes/no flags, one per bit. Give each flag its own bit with a shift, then:

  • Set a bit: flags |= BIT;
  • Clear a bit: flags &= ~BIT;
  • Toggle a bit: flags ^= BIT;
  • Test a bit: if (flags & BIT)
File permissions as bit flags
#include <stdio.h>

#define READ  (1 << 0)   // 001
#define WRITE (1 << 1)   // 010
#define EXEC  (1 << 2)   // 100

void show(int p) {
    printf("%c%c%c\n",
           (p & READ)  ? 'r' : '-',
           (p & WRITE) ? 'w' : '-',
           (p & EXEC)  ? 'x' : '-');
}

int main(void) {
    int perms = 0;
    perms |= READ;            // set
    perms |= WRITE;           // set
    show(perms);
    perms &= ~WRITE;          // clear
    perms ^= EXEC;            // toggle on
    show(perms);
    return 0;
}
output
rw-
r-x

Run and edit it in the app.

(#define makes a named constant — you'll meet it properly in the preprocessor lesson.)

Watch it

A classic: is it odd?

The lowest bit of every odd number is 1. So n & 1 is 1 for odd numbers and 0 for even ones — the same answer as n % 2, by looking at one bit.

What is 6 ^ 3 ? (6 is 110, 3 is 011)

Common mistake

Forgetting brackets. Bitwise & binds more loosely than ==, so if (flags & READ == READ) means flags & (READ == READ) — almost never what you want. Write if ((flags & READ) == READ), or just if (flags & READ).

Also: don't shift by as many bits as the type has, or more (1 << 32 on a 32-bit int) — and don't shift negative numbers. Both are undefined behaviour.

Think of it like

A row of light switches. | flips chosen switches on, & ~ flips them off, ^ flips them to the opposite, and & peeks to see whether one is on.

Key terms

  • Bit — A single binary digit, 0 or 1.
  • Bitwise operator — Works on each bit: & | ^ ~ << >>.
  • Mask — A number with just the bits you care about set, like WRITE.
  • Flag — One bit used as a yes/no switch.

Check yourself

What is 12 & 10? (1100 and 1010)

What does x << 1 do to a positive x?

Which line switches OFF the WRITE bit in flags?