Master bitwise operators in C (&, |, ^, ~, <<, >>). Learn bit masking, setting, clearing, toggling bits, and high-performance register tricks.
In C programming, Bitwise Operators allow developers to manipulate individual binary bits (0s and 1s) directly inside CPU registers.
Because CPUs execute bitwise operations in a single clock cycle, bit manipulation is the foundation of high-performance computing, device drivers, embedded microcontrollers, cryptography, network protocols, and game engine optimizations.
In this comprehensive guide, you will master all 6 bitwise operators in C, learn the fundamental "Big 4" Bit Masking techniques (Set, Clear, Toggle, Check), discover famous algorithmic bit hacks (like Brian Kernighan's population count and power-of-two tests), and explore runnable C code examples.
1. The 6 Bitwise Operators in C: Truth Tables
Standard C provides 6 bitwise operators that operate on integer types (char, short, int, long, signed and unsigned):
| Operator | Name | Syntax | Description | Example (A=5 (0101_2), B=3 (0011_2)) |
|---|---|---|---|---|
& | Bitwise AND | A & B | Result bit is 1 only if both operand bits are 1 | 5 & 3 $→$ 0001 (1) |
| | Bitwise OR | A | B | Result bit is 1 if either operand bit is 1 | 5 | 3 $→$ 0111 (7) |
^ | Bitwise XOR | A ^ B | Result bit is 1 if bits are different | 5 ^ 3 $→$ 0110 (6) |
~ | Bitwise NOT | ~A | Inverts all bits (0 becomes 1, 1 becomes 0) | ~5 $→$ ...11111010 (-6) |
<< | Left Shift | A << n | Shifts bits left by n positions (multiplies by $2^n$) | 5 << 1 $→$ 1010 (10) |
>> | Right Shift | A >> n | Shifts bits right by n positions (divides by $2^n$) | 5 >> 1 $→$ 0010 (2) |
2. Visualizing Truth Tables
text (ISO Standard)Bit A | Bit B | A & B (AND) | A | B (OR) | A ^ B (XOR) | ~A (NOT) -------+-------+-------------+------------+-------------+---------- 0 | 0 | 0 | 0 | 0 | 1 0 | 1 | 0 | 1 | 1 | 1 1 | 0 | 0 | 1 | 1 | 0 1 | 1 | 1 | 1 | 0 | 0
3. The "Big 4" Bit Masking Recipes
Bit masking is the technique of using a specific pattern of bits (a mask) along with bitwise operators to modify or inspect specific bit positions without altering surrounding data.
Recipe 1: Setting the n-th Bit (Force to 1)
To force bit n to 1, create a mask with (1 << n) and apply Bitwise OR (|):
c (ISO Standard)// Set bit at index n (0-indexed) number |= (1 << n);
text (ISO Standard)Number: 0010 0100 (36) Mask: 0000 0010 (1 << 1) ----------------------------- OR Result: 0010 0110 (38) <-- Bit 1 is now 1
Recipe 2: Clearing the n-th Bit (Force to 0)
To force bit n to 0, invert the mask with ~(1 << n) and apply Bitwise AND (&):
c (ISO Standard)// Clear bit at index n number &= ~(1 << n);
text (ISO Standard)Number: 0010 0110 (38) Inverted: 1111 1101 (~(1 << 1)) ----------------------------- AND Result: 0010 0100 (36) <-- Bit 1 is now 0
Recipe 3: Toggling the n-th Bit (Flip 0 <-> 1)
To flip the state of bit n, apply Bitwise XOR (^) with (1 << n):
c (ISO Standard)// Toggle bit at index n number ^= (1 << n);
Recipe 4: Checking / Reading the n-th Bit
To inspect if bit n is 1 or 0:
c (ISO Standard)// Returns 1 if set, 0 if cleared int isSet = (number >> n) & 1; // Alternatively: int isSetBool = (number & (1 << n)) != 0;
4. 5 High-Performance Bitwise Algorithms & Hacks
Hack 1: Check if an Integer is a Power of Two
A power of two in binary has exactly one bit set (e.g., $8 = 1000_2$). Subtracting 1 flips all lower bits ($7 = 0111_2$). Applying n & (n - 1) clears the single set bit to 0:
c (ISO Standard)#include <stdbool.h> bool isPowerOfTwo(unsigned int n) { return (n > 0) && ((n & (n - 1)) == 0); }
Hack 2: Swap Two Variables Without a Temporary Variable
Using the self-inverting property of XOR (x ⊕ x = 0 and x ⊕ 0 = x):
c (ISO Standard)void xorSwap(int *a, int *b) { if (a != b) { // Guard against same memory address *a ^= *b; *b ^= *a; *a ^= *b; } }
Hack 3: Check Even vs. Odd Fast
The least significant bit (LSB, bit 0) of every odd number is 1. Checking n & 1 avoids CPU division/modulo instructions:
c (ISO Standard)bool isOdd(int n) { return (n & 1); // 1 = Odd, 0 = Even }
Hack 4: Count Set Bits (Brian Kernighan’s Algorithm)
Brian Kernighan’s algorithm runs in $O(set bits)$ time by clearing the lowest set bit on every iteration:
c (ISO Standard)int countSetBits(unsigned int n) { int count = 0; while (n > 0) { n &= (n - 1); // Clears the lowest set bit count++; } return count; }
(In GCC, you can also use the hardware-accelerated intrinsic __builtin_popcount(n)).
Hack 5: High-Speed Multiplication & Division by Powers of Two
x << 3is equivalent to $x × 8$ ($x × 2^3$).x >> 2is equivalent to $x / 4$ ($x / 2^2$).
5. Complete Runnable C Program
c (ISO Standard)#include <stdio.h> #include <stdbool.h> void printBinary(unsigned char num) { for (int i = 7; i >= 0; i--) { printf("%d", (num >> i) & 1); } printf(" (Decimal: %d)\n", num); } int main(void) { unsigned char reg = 0b00100100; // 36 printf("Initial value: "); printBinary(reg); // 1. Set bit 1 reg |= (1 << 1); printf("After setting bit 1: "); printBinary(reg); // 00100110 (38) // 2. Toggle bit 5 reg ^= (1 << 5); printf("After toggling bit 5:"); printBinary(reg); // 00000110 (6) // 3. Clear bit 2 reg &= ~(1 << 2); printf("After clearing bit 2:"); printBinary(reg); // 00000010 (2) // 4. Test bit status printf("\nIs bit 1 set? %s\n", ((reg >> 1) & 1) ? "YES" : "NO"); printf("Is bit 0 set? %s\n", ((reg >> 0) & 1) ? "YES" : "NO"); return 0; }
Frequently Asked Questions (FAQ)
1. What is the difference between Logical (&&, ||) and Bitwise (&, |) operators?
- Logical Operators (
&&,||): Evaluate entire boolean expressions (trueorfalse) and feature short-circuit evaluation (e.g., if the left side of&&is false, the right side is never evaluated). - Bitwise Operators (
&,|): Operate on every individual bit in parallel and never short-circuit.
2. What is the difference between Arithmetic and Logical Right Shift?
- Logical Right Shift (
>>onunsigned int): Shifts bits right and always fills vacated high-order bits with0. - Arithmetic Right Shift (
>>onsigned int): Shifts bits right and replicates the sign bit (1for negative numbers,0for positive numbers) to preserve algebraic sign.
3. Can bit shifting cause Undefined Behavior in C?
Yes. Shifting by a negative count (e.g., x << -1) or by an amount greater than or equal to the bit width of the integer (e.g., 1 << 32 on a 32-bit int) triggers ISO C undefined behavior.
Conclusion
Bitwise operators provide direct, low-level control over binary data. By mastering the core masking operations (|, &, ^, ~) and utilizing shift arithmetic, you can write concise, lightning-fast C code for embedded systems, algorithms, and system software.
Related Technical Resources
- Control embedded hardware with The Volatile Keyword in C — Embedded & Hardware Guide.
- Optimize compiler output with GCC Compiler Flags Guide — Warnings, Optimization & Debug.