🌱 C Basics
Bitwise operators: surgery on individual bits
▶ Open the interactive lesson — free, no signupThe 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.
| op | name | rule per bit | example (8-bit) |
|---|---|---|---|
& | AND | 1 only if both are 1 | 1100 & 1010 → 1000 |
| | OR | 1 if either is 1 | 1100 | 1010 → 1110 |
^ | XOR | 1 if the bits differ | 1100 ^ 1010 → 0110 |
~ | NOT | flip every bit | ~00001111 → 11110000 |
<< | left shift | slide bits left, fill with 0 | 00000101 << 2 → 00010100 |
>> | right shift | slide bits right | 00010100 >> 2 → 00000101 |
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.
#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;
}$ 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:
#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;
}$ 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
n & 1— is n odd? (checks the ones bit; no division needed)x & 0xFF— keep only the low byte(x >> 8) & 0xFF— extract the second byte (remember the color example from the hex lesson?)x & (x - 1)— clear the lowest set bit; zero iff x was a power of two
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)