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 value | 8 | 4 | 2 | 1 |
|---|---|---|---|---|
| 13 in binary | 1 | 1 | 0 | 1 |
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:
| Operator | Name | A result bit is 1 when… | 12 op 10 |
|---|---|---|---|
& | AND | both bits are 1 | 1100 & 1010 = 1000 = 8 |
| | OR | either bit is 1 | 1100 | 1010 = 1110 = 14 |
^ | XOR | the bits differ | 1100 ^ 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 |
#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 = 3Run 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:
#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 6Run 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)
#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-xRun 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?