Bit manipulation in C means reading, setting, clearing, and toggling individual bits within an integer. At the hardware level, everything from GPIO port states to network protocol flags to image pixel data is ultimately a pattern of bits. Knowing how to work with them efficiently — using only bitwise operators with no branches — is an essential skill for systems, embedded, and competitive programming.
The Six Bitwise Operators
| Operator | Symbol | Action | Example (8-bit) |
|---|---|---|---|
| AND | & |
1 only when both bits are 1 | 0b1100 & 0b1010 = 0b1000 |
| OR | | |
1 when either bit is 1 | 0b1100 | 0b1010 = 0b1110 |
| XOR | ^ |
1 when bits differ | 0b1100 ^ 0b1010 = 0b0110 |
| NOT | ~ |
Flip all bits | ~0b00001100 = 0b11110011 |
| Left shift | << |
Shift bits left, fill 0 from right | 0b0001 << 3 = 0b1000 |
| Right shift | >> |
Shift bits right (arithmetic/logical) | 0b1000 >> 2 = 0b0010 |
Always use unsigned integers for bit manipulation. Right-shifting a signed negative number is implementation-defined in C. Left-shifting into the sign bit causes undefined behaviour. Using unsigned int avoids both problems.
The Core Bit Manipulation Patterns
Four operations — set, clear, toggle, check — cover nearly every bit manipulation task. Each takes a value x and a bit position n (0 = least-significant bit):
Pattern Formula Mnemonic ────────────────────────────────────────────────────────── Set bit n x | (1u << n) OR with a 1 turns it on Clear bit n x & ~(1u << n) AND with a 0 forces it off Toggle bit n x ^ (1u << n) XOR flips whatever is there Check bit n (x >> n) & 1u shift to LSB, mask with 1
Bit Manipulation Program in C
#include <stdio.h>
/* Print the low 8 bits of x in binary */
static void print_bits(unsigned int x)
{
int i;
for (i = 7; i >= 0; i--)
putchar(((x >> i) & 1u) ? '1' : '0');
}
/* ── Core four ───────────────────────────────────────────── */
unsigned int set_bit (unsigned int x, int n) { return x | (1u << n); }
unsigned int clear_bit (unsigned int x, int n) { return x & ~(1u << n); }
unsigned int toggle_bit(unsigned int x, int n) { return x ^ (1u << n); }
int check_bit (unsigned int x, int n) { return (x >> n) & 1u; }
/* ── Count set bits — Brian Kernighan's algorithm ─────────── */
/* Each iteration clears the lowest set bit: x &= (x-1) */
int count_set_bits(unsigned int x)
{
int count = 0;
while (x) {
x &= x - 1u; /* clears lowest set bit */
count++;
}
return count;
}
/* ── Check if x is a power of 2 ────────────────────────────── */
/* A power of 2 has exactly one set bit. */
/* x-1 flips all bits below that bit, so x & (x-1) == 0. */
int is_power_of_two(unsigned int x)
{
return x && !(x & (x - 1u));
}
/* ── Isolate the lowest set bit ────────────────────────────── */
/* Two's complement: -x flips all bits and adds 1. */
/* x & (-x) keeps only the lowest set bit. */
unsigned int lowest_set_bit(unsigned int x)
{
return x & (unsigned int)(-(int)x);
}
/* ── Swap two integers without a temp variable ─────────────── */
/* Only safe when a and b are distinct memory locations. */
void xor_swap(int *a, int *b)
{
if (a != b) {
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
}
/* ── Extract n bits starting at position p ─────────────────── */
/* Shift right to align, then mask with n ones. */
unsigned int extract_bits(unsigned int x, int p, int n)
{
return (x >> p) & ~(~0u << n);
}
/* ── Reverse the bits of an 8-bit value ────────────────────── */
unsigned int reverse_bits_8(unsigned int x)
{
unsigned int r = 0;
int i;
for (i = 0; i < 8; i++) {
r = (r << 1) | (x & 1u);
x >>= 1;
}
return r & 0xFF;
}
int main(void)
{
unsigned int x = 0x2C; /* 0b00101100 = 44 decimal */
int a = 15, b = 27;
printf("x = 0x%02X = ", x); print_bits(x); printf(" (%u)\n\n", x);
/* Set, clear, toggle, check */
printf("set_bit(x, 0) = "); print_bits(set_bit(x, 0));
printf(" (bit 0 set)\n");
printf("clear_bit(x, 3) = "); print_bits(clear_bit(x, 3));
printf(" (bit 3 cleared)\n");
printf("toggle_bit(x, 5) = "); print_bits(toggle_bit(x, 5));
printf(" (bit 5 toggled)\n");
printf("check_bit(x, 2) = %d (bit 2 is %s)\n\n",
check_bit(x, 2), check_bit(x, 2) ? "set" : "clear");
/* Count set bits */
printf("count_set_bits(0x2C) = %d\n", count_set_bits(x));
printf("count_set_bits(0xFF) = %d\n\n", count_set_bits(0xFF));
/* Power of two */
printf("is_power_of_two(16) = %d\n", is_power_of_two(16));
printf("is_power_of_two(18) = %d\n\n", is_power_of_two(18));
/* Lowest set bit */
printf("x = 0b00101100, lowest_set_bit = ");
print_bits(lowest_set_bit(x)); printf("\n\n");
/* XOR swap */
printf("Before swap: a=%d, b=%d\n", a, b);
xor_swap(&a, &b);
printf("After swap: a=%d, b=%d\n\n", a, b);
/* Extract bits */
printf("extract_bits(0x2C, p=2, n=3) = %u (3 bits from pos 2)\n\n",
extract_bits(x, 2, 3));
/* Reverse bits */
printf("reverse_bits_8(0b00101100) = ");
print_bits(reverse_bits_8(x)); printf("\n");
return 0;
}
How to Compile and Run
gcc -ansi -Wall -Wextra -o bitmani bitmani.c
./bitmani
Sample Output
x = 0x2C = 00101100 (44) set_bit(x, 0) = 00101101 (bit 0 set) clear_bit(x, 3) = 00100100 (bit 3 cleared) toggle_bit(x, 5) = 00001100 (bit 5 toggled) check_bit(x, 2) = 1 (bit 2 is set) count_set_bits(0x2C) = 3 count_set_bits(0xFF) = 8 is_power_of_two(16) = 1 is_power_of_two(18) = 0 x = 0b00101100, lowest_set_bit = 00000100 Before swap: a=15, b=27 After swap: a=27, b=15 extract_bits(0x2C, p=2, n=3) = 3 (3 bits from pos 2) reverse_bits_8(0b00101100) = 00110100
Visual Trace: Why x &= (x-1) Counts Bits
x = 0b00101100 (3 set bits)
Iteration 1: x - 1 = 0b00101011
x & (x-1) = 0b00101100 & 0b00101011 = 0b00101000 count=1
Iteration 2: x - 1 = 0b00100111
x & (x-1) = 0b00101000 & 0b00100111 = 0b00100000 count=2
Iteration 3: x - 1 = 0b00011111
x & (x-1) = 0b00100000 & 0b00011111 = 0b00000000 count=3
x == 0, loop ends. Answer: 3 ✓
Each iteration strips exactly the lowest set bit, so the loop runs exactly as many times as there are set bits — even for sparse numbers like 0x80000000 (one iteration instead of 32).
Visual Trace: Why x & (x-1) == 0 Detects Powers of Two
x = 16 = 0b00010000 (power of 2 — exactly one set bit) x - 1 = 15 = 0b00001111 x & (x-1) = 0b00010000 & 0b00001111 = 0b00000000 → IS a power of 2 ✓ x = 18 = 0b00010010 (not a power of 2 — two set bits) x - 1 = 17 = 0b00010001 x & (x-1) = 0b00010010 & 0b00010001 = 0b00010000 → NOT a power of 2 ✓
Bit Manipulation in Embedded C
Embedded systems use bit manipulation constantly to control hardware registers. A typical pattern for a GPIO register:
/* Conceptual example — actual register addresses are MCU-specific */
#define GPIO_BASE 0x40020000UL
#define GPIO_MODER (*(volatile unsigned int *)(GPIO_BASE + 0x00))
#define GPIO_ODR (*(volatile unsigned int *)(GPIO_BASE + 0x14))
#define LED_PIN 5
/* Set pin 5 as output (mode = 0b01 in bits [11:10]) */
GPIO_MODER &= ~(0x3u << (LED_PIN * 2)); /* clear 2 bits */
GPIO_MODER |= (0x1u << (LED_PIN * 2)); /* set output mode */
/* Toggle LED on pin 5 */
GPIO_ODR ^= (1u << LED_PIN);
The same four patterns — set, clear, toggle, check — appear in every embedded codebase. The only difference from the pure-C examples above is the volatile qualifier on the register pointer, which prevents the compiler from caching or eliminating the read/write.
Common Mistakes
- Using
intinstead ofunsigned int: left-shifting a 1 into the sign bit of a signed integer is undefined behaviour in C. Write1u << n, not1 << n. - Shifting by a negative amount or by ≥ bit width: both are undefined behaviour. Always validate
nis in range before shifting. - XOR swap on aliased pointers:
xor_swap(&a, &a)setsato zero. Thea != bguard handles this. - Forgetting operator precedence:
&,|, and^have lower precedence than==. Always parenthesise:(x & mask) == mask, notx & mask == mask.
Time and Space Complexity
| Operation | Time | Notes |
|---|---|---|
| Set / Clear / Toggle / Check | O(1) | Single instruction on hardware |
| Count set bits (Kernighan) | O(k) | k = number of set bits — optimal for sparse values |
| Count set bits (naive loop) | O(w) | w = bit width (32 or 64) |
| Is power of two | O(1) | Two operations |
| Reverse bits | O(w) | w iterations; lookup table gives O(1) per byte |
What This Teaches
- The mask pattern:
1u << ncreates a “probe” with exactly one bit set at position n. AND with a mask isolates bits; OR with a mask sets them; XOR flips them; AND with the complement clears them. - The subtract trick:
x - 1flips the lowest set bit from 1 to 0 and flips all bits below it from 0 to 1. Combining withx & (x-1)strips the lowest set bit — the foundation of Kernighan’s algorithm and the power-of-two check. - Two’s complement and bitwise NOT:
-x == ~x + 1in two’s complement, sox & (-x)isolates the lowest set bit. - Why embedded programmers love bit manipulation: a single 32-bit register can hold 32 flags, 8 nibbles, or 4 bytes — packed to fit in a cache line and read/written in one instruction.
Related Programs
- extern Keyword in C – Multi-File Programs
- Multiply by 4 Using Bit Shift in C
- K&R Exercise 2-9 — Count Set Bits Using x &= (x-1)
- K&R Exercise 2-6 — setbits
- C Aptitude Questions and Answers
As an Amazon Associate we earn from qualifying purchases.
Recommended Book
Chapter 2 of The C Programming Language by Kernighan & Ritchie covers bitwise operators, and exercises 2-6 through 2-9 are classic bit manipulation problems — all solved on this site. Also on Amazon.com.