C course menu
Bitwise Operators in C: AND, OR, XOR, NOT, Shifts and Bit Masks
Learn bitwise operations in C: the &, |, ^, ~, << and >> operators, setting, clearing, toggling and testing bits with masks, flags packed into one integer, fixed-width types from stdint.h, counting set bits, and classic bit tricks used in embedded systems and interviews.
On this page
Bitwise operators work on the individual bits of integers. They are essential in embedded systems (where a single register bit can switch on an LED or a motor), in networking and file formats, for compact sets of flags, and in many interview questions.
The operators#
#include <stdio.h>
void print_bits(unsigned char x) {
for (int i = 7; i >= 0; i--) putchar((x >> i) & 1 ? '1' : '0');
}
int main(void) {
unsigned char a = 0xCA, b = 0x6C; /* 11001010 and 01101100 */
printf("a "); print_bits(a); printf("\n");
printf("b "); print_bits(b); printf("\n");
printf("a & b "); print_bits(a & b); printf("\n");
printf("a | b "); print_bits(a | b); printf("\n");
printf("a ^ b "); print_bits(a ^ b); printf("\n");
printf("~a "); print_bits((unsigned char)~a); printf("\n");
printf("a << 1 "); print_bits((unsigned char)(a << 1)); printf("\n");
printf("a >> 2 "); print_bits(a >> 2); printf("\n");
return 0;
}a 11001010
b 01101100
a & b 01001000
a | b 11101110
a ^ b 10100110
~a 00110101
a << 1 10010100
a >> 2 00110010x << nmultiplies by 2ⁿ andx >> ndivides by 2ⁿ for non-negative values, as long as nothing overflows.- Use unsigned types for bit manipulation. Shifting a negative number right, or shifting a 1 into a signed number's sign bit, has implementation-defined or undefined results.
- Do not confuse
&and|with the logical&&and||.
Fixed-width types#
The size of int varies between platforms, so bit-level code usually uses the exact-width types from <stdint.h>: uint8_t, uint16_t, uint32_t, uint64_t (and signed int32_t and so on).
Masks: set, clear, toggle and test#
A mask selects bits. 1u << n is a mask with only bit n set:
#include <stdio.h>
#include <stdint.h>
int main(void) {
uint8_t reg = 0; /* imagine an 8-bit hardware register */
reg |= 1u << 3; /* set bit 3 */
reg |= 1u << 0; /* set bit 0 */
printf("after set: 0x%02X\n", reg);
reg &= (uint8_t)~(1u << 0); /* clear bit 0 */
printf("after clear: 0x%02X\n", reg);
reg ^= 1u << 7; /* toggle bit 7 */
printf("after toggle: 0x%02X\n", reg);
printf("bit 3 is %s\n", (reg & (1u << 3)) ? "on" : "off");
printf("bit 4 is %s\n", (reg & (1u << 4)) ? "on" : "off");
return 0;
}after set: 0x09
after clear: 0x08
after toggle: 0x88
bit 3 is on
bit 4 is offFlags#
Several yes/no options fit in one integer, each one a power of two. This is how many APIs pass options, such as file permissions on Unix:
#include <stdio.h>
enum {
PERM_READ = 1u << 0,
PERM_WRITE = 1u << 1,
PERM_EXEC = 1u << 2,
};
void describe(unsigned perms) {
printf("%c%c%c\n",
perms & PERM_READ ? 'r' : '-',
perms & PERM_WRITE ? 'w' : '-',
perms & PERM_EXEC ? 'x' : '-');
}
int main(void) {
unsigned user = PERM_READ | PERM_WRITE;
describe(user);
user |= PERM_EXEC;
user &= ~PERM_WRITE;
describe(user);
return 0;
}rw-
r-xExtracting fields#
Shift, then mask, to read a group of bits. For example, the red, green and blue parts of a 24-bit colour:
#include <stdio.h>
#include <stdint.h>
int main(void) {
uint32_t colour = 0x3F6283; /* the C course's accent colour */
unsigned r = (colour >> 16) & 0xFF;
unsigned g = (colour >> 8) & 0xFF;
unsigned b = colour & 0xFF;
printf("r=%u g=%u b=%u\n", r, g, b);
uint32_t packed = (r << 16) | (g << 8) | b;
printf("0x%06X\n", (unsigned)packed);
return 0;
}r=63 g=98 b=131
0x3F6283Classic bit tricks#
#include <stdio.h>
int count_bits(unsigned x) {
int count = 0;
while (x) {
x &= x - 1; /* clears the lowest set bit */
count++;
}
return count;
}
int is_power_of_two(unsigned x) {
return x != 0 && (x & (x - 1)) == 0;
}
int main(void) {
printf("%d %d\n", count_bits(0xF0u), count_bits(255u));
printf("%d %d %d\n", is_power_of_two(64), is_power_of_two(96), is_power_of_two(0));
printf("odd? %u\n", 37u & 1u);
int a = 5, b = 9;
a ^= b; b ^= a; a ^= b; /* swap without a temporary: a curiosity, not faster */
printf("%d %d\n", a, b);
return 0;
}4 8
1 0 0
odd? 1
9 5x & (x - 1) removes the lowest 1 bit, so a power of two (exactly one bit set) becomes 0. The XOR swap is a well-known interview puzzle, but a normal swap with a temporary is clearer and at least as fast.
Practice#
Practice exercise
Write unsigned reverse_bits8(unsigned x) that reverses the lowest 8 bits of x (bit 0 becomes bit 7, and so on), and test it on 0x01, 0xF0 and 0xB4. Print the results in hexadecimal.
Show solution
#include <stdio.h>
unsigned reverse_bits8(unsigned x) {
unsigned result = 0;
for (int i = 0; i < 8; i++) {
result = (result << 1) | ((x >> i) & 1u);
}
return result;
}
int main(void) {
printf("0x%02X 0x%02X 0x%02X\n", reverse_bits8(0x01), reverse_bits8(0xF0), reverse_bits8(0xB4));
return 0;
}0x80 0x0F 0x2DKey takeaway#
Finished this lesson?