C Bitwise Operators
Working with individual bits in binary numbers
🔢 What are Bitwise Operators?
Bitwise operators work on individual bits of numbers in binary form. They perform operations like AND, OR, XOR on each bit position, useful for low-level programming and optimization.
// Simple bitwise example
int a = 5; // Binary: 101
int b = 3; // Binary: 011
int result = a & b; // AND operation: 001 = 1
printf("Result: %d", result);
Types of Bitwise Operators
AND (&)
Returns 1 if both bits are 1
OR (|)
Returns 1 if any bit is 1
XOR (^)
Returns 1 if bits are different
Shift (<< >>)
Move bits left or right
🔹 Bitwise AND (&)
The bitwise AND operator (&) performs a logical AND operation on each corresponding pair of bits from two operands, returning 1 only when both bits are 1. For example, 0b1100 & 0b1010 results in 0b1000 because only the fourth bit from the right is 1 in both operands. This operator is extensively used for masking operations where specific bits need to be isolated or cleared. Common applications include checking if specific flags are set with if (flags & FLAG_ENABLED), extracting bit fields from packed data structures, implementing permission systems, and filtering data in low-level hardware programming where individual bits represent distinct hardware states or configuration options.
#include
int main() {
int a = 12; // Binary: 1100
int b = 10; // Binary: 1010
int result = a & b; // Binary: 1000 = 8
printf("a = %d (Binary: 1100)\n", a);
printf("b = %d (Binary: 1010)\n", b);
printf("a & b = %d (Binary: 1000)\n", result);
// Practical use: Check if number is even
int num = 15;
if (num & 1) {
printf("%d is odd\n", num);
} else {
printf("%d is even\n", num);
}
return 0;
}
Output:
b = 10 (Binary: 1010)
a & b = 8 (Binary: 1000)
15 is odd
🔹 Bitwise OR (|)
The bitwise OR operator (|) performs a logical OR operation on corresponding bits, returning 1 when at least one of the bits in that position is 1. For instance, 0b1100 | 0b1010 produces 0b1110 since any position where either operand has a 1 bit results in a 1 in the output. This operator is primarily used for setting specific bits without affecting others, combining multiple flags or options, and merging bit patterns. Typical use cases include enabling feature flags with options |= NEW_FLAG, building composite permissions, constructing bit masks, and configuring hardware registers in embedded systems where multiple control bits need to be set simultaneously while preserving other register values.
#include
int main() {
int a = 12; // Binary: 1100
int b = 10; // Binary: 1010
int result = a | b; // Binary: 1110 = 14
printf("a = %d (Binary: 1100)\n", a);
printf("b = %d (Binary: 1010)\n", b);
printf("a | b = %d (Binary: 1110)\n", result);
// Practical use: Set specific bits
int flags = 0; // Binary: 0000
flags = flags | 4; // Set bit 2: 0100
flags = flags | 1; // Set bit 0: 0101
printf("Flags: %d (Binary: 0101)\n", flags);
return 0;
}
Output:
b = 10 (Binary: 1010)
a | b = 14 (Binary: 1110)
Flags: 5 (Binary: 0101)
🔹 Bitwise XOR (^)
The bitwise XOR (exclusive OR) operator (^) returns 1 only when corresponding bits differ, making it useful for toggling and detecting differences. For example, 0b1100 ^ 0b1010 yields 0b0110 because bits differ in the second and third positions from the right. XOR has unique properties: any value XORed with itself equals zero, and XOR operations are reversible, making them perfect for simple encryption, checksums, and data verification. Common applications include toggling flags with flags ^= TOGGLE_BIT, swapping variables without temporary storage, implementing fast parity checks, detecting changed bits, and creating lightweight encryption schemes in embedded systems where computational resources are limited.
#include
int main() {
int a = 12; // Binary: 1100
int b = 10; // Binary: 1010
int result = a ^ b; // Binary: 0110 = 6
printf("a = %d (Binary: 1100)\n", a);
printf("b = %d (Binary: 1010)\n", b);
printf("a ^ b = %d (Binary: 0110)\n", result);
// Practical use: Simple encryption/decryption
int original = 65; // 'A'
int key = 42;
int encrypted = original ^ key;
int decrypted = encrypted ^ key;
printf("Original: %d, Encrypted: %d, Decrypted: %d\n",
original, encrypted, decrypted);
return 0;
}
Output:
b = 10 (Binary: 1010)
a ^ b = 6 (Binary: 0110)
Original: 65, Encrypted: 107, Decrypted: 65
🔹 Shift Operators (<< >>)
Shift operators move bits left (<<) or right (>>) by a specified number of positions, providing efficient multiplication and division by powers of two. Left shift x << n multiplies x by 2^n by shifting bits left and filling right positions with zeros, while right shift x >> n divides by 2^n. For example, 5 << 2 equals 20 (5 * 4), and 20 >> 2 equals 5. Right shifts are either arithmetic (preserving sign bit) or logical (filling with zeros) depending on whether the type is signed or unsigned. These operators are crucial for bit manipulation, creating bit masks, extracting specific bit ranges, implementing fast arithmetic, and accessing packed data structures in systems programming.
#include
int main() {
int num = 5; // Binary: 101
printf("Original: %d (Binary: 101)\n", num);
// Left shift: multiply by 2^n
int left_shift = num << 1; // Binary: 1010 = 10
printf("Left shift by 1: %d (Binary: 1010)\n", left_shift);
int left_shift2 = num << 2; // Binary: 10100 = 20
printf("Left shift by 2: %d (Binary: 10100)\n", left_shift2);
// Right shift: divide by 2^n
int big_num = 20; // Binary: 10100
int right_shift = big_num >> 1; // Binary: 1010 = 10
printf("Right shift by 1: %d (Binary: 1010)\n", right_shift);
int right_shift2 = big_num >> 2; // Binary: 101 = 5
printf("Right shift by 2: %d (Binary: 101)\n", right_shift2);
return 0;
}
Output:
Left shift by 1: 10 (Binary: 1010)
Left shift by 2: 20 (Binary: 10100)
Right shift by 1: 10 (Binary: 1010)
Right shift by 2: 5 (Binary: 101)
🔹 Bitwise NOT (~)
The bitwise NOT operator (~) is a unary operator that inverts all bits in its operand, converting each 1 to 0 and each 0 to 1. For example, if x = 0b00001111 (assuming 8-bit representation), then ~x produces 0b11110000. This operator is essential for creating bit masks, clearing specific bits when combined with AND, and implementing one's complement operations. Typical uses include creating inverted masks with ~MASK to clear specific bits, generating all-ones patterns for bit testing, implementing bitwise logical operations, and working with hardware registers where certain bits need to be inverted. Be cautious with signed integers as the sign bit is also flipped, potentially causing unexpected results.
#include
int main() {
unsigned int num = 5; // Binary: 00000101
unsigned int result = ~num; // Flips all bits
printf("Original: %u\n", num);
printf("After NOT: %u\n", result);
// With smaller example for clarity
unsigned char small = 5; // Binary: 00000101
unsigned char flipped = ~small; // Binary: 11111010 = 250
printf("Small original: %u (Binary: 00000101)\n", small);
printf("Small flipped: %u (Binary: 11111010)\n", flipped);
return 0;
}
Output:
After NOT: 4294967290
Small original: 5 (Binary: 00000101)
Small flipped: 250 (Binary: 11111010)