The C Path — learn C, visually

🌱 C Basics

Bitwise operators: surgery on individual bits

⏱ 14 min · free interactive lesson · quizzes, visualizations & a real compiler

▶ Open the interactive lesson — free, no signup
Why you're learning this

The hex color #FF8800 you decoded in Part 0 is really three small numbers squeezed into one, and a file permission like chmod 644 is three on/off switches per digit. Bitwise operators are the tools for packing, unpacking, and flipping those hidden pieces — you'll finish by rebuilding the exact permissions trick Unix has used for fifty years.

Part 0 taught you that everything is bits. Now C hands you the scalpel: six bitwise operators that let you read and flip each individual bit of a number without disturbing its neighbors. They power the code that talks directly to hardware, file formats, compression, cryptography — and every byte of packed on/off switches (flags) you'll meet in real code.

opnamerule per bitexample (8-bit)
&AND1 only if both are 11100 & 10101000
|OR1 if either is 11100 | 10101110
^XOR1 if the bits differ1100 ^ 10100110
~NOTflip every bit~0000111111110000
<<left shiftslide bits left, fill with 000000101 << 200010100
>>right shiftslide bits right00010100 >> 200000101
⚠️

Don't confuse &/| (bitwise, works on every bit) with &&/|| (logical, works on whole truth values). 1 & 2 is 0 (no common bits!) but 1 && 2 is 1 (both nonzero). Mixing them up compiles fine and fails weirdly.

bitwise.c
#include <stdio.h>

int main(void) {
    unsigned int a = 0xC, b = 0xA;   /* 1100 and 1010 */

    printf("a & b  = 0x%X\n", a & b);    /* AND: 1000 */
    printf("a | b  = 0x%X\n", a | b);    /* OR : 1110 */
    printf("a ^ b  = 0x%X\n", a ^ b);    /* XOR: 0110 */
    printf("~a     = 0x%X\n", ~a);       /* flip all 32 bits */
    printf("1 << 4 = %u\n", 1u << 4);    /* 16: bit 4 set  */
    printf("80 >> 3 = %u\n", 80u >> 3);  /* 80 / 8 = 10    */
    return 0;
}
terminal
$ gcc bitwise.c -o bitwise && ./bitwise
a & b  = 0x8
a | b  = 0xE
a ^ b  = 0x6
~a     = 0xFFFFFFF3
1 << 4 = 16
80 >> 3 = 10

🧠 Checkpoint: What is 5 & 3?

  • 7
  • 2
  • 1
  • 0
Show answer

1 — 5 = 101, 3 = 011. AND keeps positions where BOTH have a 1 — only the ones bit: 001 = 1. (5 | 3 would be 111 = 7, and 5 ^ 3 = 110 = 6.)

Shifts are multiplication and division

Shifting left by n multiplies by 2ⁿ (each bit's place value doubles per step); shifting right divides by 2ⁿ, discarding the remainder. 1 << n is THE idiom for "a number with only bit n set" — you'll write it constantly.

💀

Shift with care: left-shifting a negative number is undefined behavior, right-shifting a negative number is implementation-defined (arithmetic vs logical shift), and shifting by ≥ the type's width (e.g. x << 32 on a 32-bit int) is UB too. Habit to build: do bit twiddling on unsigned types.

The four moves of bit surgery

Say a byte holds eight on/off flags and you want to manipulate flag n without disturbing its neighbors. Four idioms cover everything:

flags.c
#include <stdio.h>

int main(void) {
    unsigned char flags = 0;       /* 00000000 */
    unsigned char BOLD  = 1u << 0; /* 00000001 */
    unsigned char CAPS  = 1u << 3; /* 00001000 */

    flags |= CAPS;                 /* SET:    force bit to 1  */
    flags |= BOLD;
    printf("after set   : 0x%02X\n", flags);

    flags &= ~BOLD;                /* CLEAR:  force bit to 0  */
    printf("after clear : 0x%02X\n", flags);

    flags ^= CAPS;                 /* TOGGLE: flip the bit    */
    printf("after toggle: 0x%02X\n", flags);

    if (flags & CAPS)              /* TEST:   is the bit on?  */
        printf("caps is ON\n");
    else
        printf("caps is OFF\n");
    return 0;
}
terminal
$ gcc flags.c -o flags && ./flags
after set   : 0x09
after clear : 0x08
after toggle: 0x00
caps is OFF

Try it with your own hands — here's a byte; the mask 1 << 3 is 00001000:

This spot has an interactive bits widget — open the interactive lesson to play with it.

🧠 Checkpoint: Which expression CLEARS bit 5 of x and leaves the rest alone?

  • x |= (1 << 5)
  • x ^= (1 << 5)
  • x &= ~(1 << 5)
  • x = ~(1 << 5)
Show answer

x &= ~(1 << 5) — ~(1<<5) is all-ones except bit 5. ANDing keeps every bit where the mask is 1 and zeroes bit 5. (|= sets; ^= toggles — it would SET the bit if it was clear!)

Idioms you'll meet in the wild

🎉

Party trick: XOR can swap two variables without a temporary: a ^= b; b ^= a; a ^= b;. It works because XOR is its own inverse (x ^ y ^ y == x). Fun to know, terrible to use — it's slower than a temp on modern CPUs and breaks if a and b are the same variable. Interviews love it anyway.

This spot has an interactive editor widget — open the interactive lesson to play with it.

🧠 Checkpoint: n << 3 computes… (for small unsigned n)

  • n × 3
  • n × 8
  • n ÷ 8
  • n + 3
Show answer

n × 8 — Each left shift doubles the value, so shifting by 3 multiplies by 2³ = 8. Compilers know this too — they turn ×8 into a shift automatically, so write whichever is clearer.

That's the full operator toolbox. Now let's put conditions to work steering your program: if and else.

▶ Practice this lesson interactively (with live gcc)