C Notes
Complete guide to bitwise operators in C: AND, OR, XOR, NOT, left shift, and right shift. Learn practical applications like flag manipulation, masking, and power-of-2 operations with examples.
Bitwise operators work directly on the binary representation of numbers, manipulating individual bits. While they might seem low-level and intimidating at first, they're incredibly powerful tools used in systems programming, embedded development, networking, graphics, and cryptography. If you want to write efficient C code, mastering bitwise operators is non-negotiable.
The Six Bitwise Operators
| Operator | Name | Description | |
|---|---|---|---|
& | Bitwise AND | Sets bit to 1 if both bits are 1 | |
| ` | ` | Bitwise OR | Sets bit to 1 if at least one bit is 1 |
^ | Bitwise XOR | Sets bit to 1 if bits are different | |
~ | Bitwise NOT | Inverts all bits (one's complement) | |
<< | Left Shift | Shifts bits to the left | |
>> | Right Shift | Shifts bits to the right |
Bitwise AND (&)
The AND operator compares each bit of two numbers. The result bit is 1 only if BOTH corresponding bits are 1.
#include <stdio.h>
int main() {
unsigned int a = 12; // 1100
unsigned int b = 10; // 1010
printf("12 & 10 = %u\n", a & b); // 1000 = 8
// Practical: Check if a number is even or odd
int num = 7;
if (num & 1) {
printf("%d is odd\n", num);
} else {
printf("%d is even\n", num);
}
// Practical: Masking - extract lower 4 bits
unsigned int value = 0xAB; // 10101011
unsigned int lower_nibble = value & 0x0F; // 00001011 = 11
printf("Lower nibble of 0xAB: %u (0x%X)\n", lower_nibble, lower_nibble);
return 0;
}12 & 10 = 8 7 is odd Lower nibble of 0xAB: 11 (0xB)
Bitwise OR (|)
The OR operator sets the result bit to 1 if EITHER or BOTH corresponding bits are 1.
#include <stdio.h>
int main() {
unsigned int a = 12; // 1100
unsigned int b = 10; // 1010
printf("12 | 10 = %u\n", a | b); // 1110 = 14
// Practical: Setting flags
unsigned int permissions = 0;
unsigned int READ = 1; // 001
unsigned int WRITE = 2; // 010
unsigned int EXEC = 4; // 100
permissions = READ | WRITE; // Set read and write
printf("Permissions (R+W): %u\n", permissions); // 011 = 3
permissions = permissions | EXEC; // Add execute
printf("Permissions (R+W+X): %u\n", permissions); // 111 = 7
return 0;
}12 | 10 = 14 Permissions (R+W): 3 Permissions (R+W+X): 7
Bitwise XOR (^)
XOR (exclusive OR) sets the result bit to 1 only when the two bits are DIFFERENT.
12 ^ 10 = 6 Before: x=25, y=42 After: x=42, y=25 5 ^ 5 = 0 5 ^ 0 = 5 Non-duplicate: 5
Bitwise NOT (~)
The NOT operator (one's complement) inverts every bit — 0 becomes 1 and 1 becomes 0.
#include <stdio.h>
int main() {
unsigned char a = 5; // 00000101
unsigned char result = ~a; // 11111010 = 250
printf("~5 (unsigned char) = %u\n", result);
// With signed integers (two's complement)
int b = 5;
printf("~5 (signed int) = %d\n", ~b); // -6
// Relationship: ~n = -(n+1)
printf("~0 = %d\n", ~0); // -1
printf("~1 = %d\n", ~1); // -2
printf("~(-1) = %d\n", ~(-1)); // 0
return 0;
}~5 (unsigned char) = 250 ~5 (signed int) = -6 ~0 = -1 ~1 = -2 ~(-1) = 0
Key insight: For signed integers, ~n = -(n+1). This is because of two's complement representation.
Left Shift Operator (<<)
Left shift moves all bits to the left by the specified number of positions. Vacated positions on the right are filled with zeros. Each left shift effectively multiplies the number by 2.
5 << 1 = 10 5 << 2 = 20 5 << 3 = 40 7 * 2 = 14 (using <<) 7 * 4 = 28 (using <<) 7 * 8 = 56 (using <<) 1 << 0 = 1 (00000001) 1 << 1 = 2 (00000010) 1 << 2 = 4 (00000100) 1 << 3 = 8 (00001000) 1 << 4 = 16 (00010000) 1 << 5 = 32 (00100000) 1 << 6 = 64 (01000000) 1 << 7 = 128 (10000000)
Right Shift Operator (>>)
Right shift moves bits to the right. For unsigned numbers, vacated positions are filled with 0. For signed numbers, the behavior depends on the implementation (usually arithmetic shift — fills with sign bit).
#include <stdio.h>
int main() {
unsigned int a = 40; // 00101000
printf("40 >> 1 = %u\n", a >> 1); // 00010100 = 20
printf("40 >> 2 = %u\n", a >> 2); // 00001010 = 10
printf("40 >> 3 = %u\n", a >> 3); // 00000101 = 5
// Right shift = divide by 2^n (integer division)
int num = 100;
printf("%d / 2 = %d (using >>)\n", num, num >> 1);
printf("%d / 4 = %d (using >>)\n", num, num >> 2);
printf("%d / 8 = %d (using >>)\n", num, num >> 3);
// Signed right shift (implementation-defined)
int negative = -16;
printf("-16 >> 1 = %d\n", negative >> 1); // Usually -8 (arithmetic shift)
return 0;
}40 >> 1 = 20 40 >> 2 = 10 40 >> 3 = 5 100 / 2 = 50 (using >>) 100 / 4 = 25 (using >>) 100 / 8 = 12 (using >>) -16 >> 1 = -8
Practical Applications
Setting, Clearing, and Toggling Bits
#include <stdio.h>
int main() {
unsigned int flags = 0;
// Set bit n: use OR with mask
flags |= (1 << 3); // Set bit 3
printf("After setting bit 3: %u\n", flags); // 8
// Clear bit n: use AND with inverted mask
flags = 15; // 1111
flags &= ~(1 << 2); // Clear bit 2
printf("After clearing bit 2 from 15: %u\n", flags); // 1011 = 11
// Toggle bit n: use XOR with mask
flags = 10; // 1010
flags ^= (1 << 1); // Toggle bit 1
printf("After toggling bit 1 of 10: %u\n", flags); // 1000 = 8
// Check if bit n is set
int value = 13; // 1101
int bit_pos = 2;
if (value & (1 << bit_pos)) {
printf("Bit %d is SET in %d\n", bit_pos, value);
}
return 0;
}After setting bit 3: 8 After clearing bit 2 from 15: 11 After toggling bit 1 of 10: 8 Bit 2 is SET in 13
Counting Set Bits (Brian Kernighan's Algorithm)
Number of set bits in 29: 4 Set bits in 7: 3 Set bits in 16: 1
Check if Number is Power of 2
1 is a power of 2 2 is a power of 2 4 is a power of 2 8 is a power of 2 16 is a power of 2
Common Bit Manipulation Tricks
| Operation | Expression | Description | |
|---|---|---|---|
| Check odd/even | n & 1 | Returns 1 if odd | |
| Multiply by 2^k | n << k | Faster than multiplication | |
| Divide by 2^k | n >> k | Faster than division | |
| Set bit i | `n | = (1 << i)` | Turn on specific bit |
| Clear bit i | n &= ~(1 << i) | Turn off specific bit | |
| Toggle bit i | n ^= (1 << i) | Flip specific bit | |
| Check bit i | n & (1 << i) | Non-zero if bit is set | |
| Power of 2 check | n & (n-1) == 0 | True for powers of 2 | |
| Turn off rightmost set bit | n & (n-1) | Kernighan's trick | |
| Isolate rightmost set bit | n & (-n) | Gets lowest set bit |
Interview Questions
Q1: How would you swap two numbers using XOR without a temporary variable?
a = a ^ b;
b = a ^ b; // b = (a^b)^b = a
a = a ^ b; // a = (a^b)^a = bThis works because XOR is its own inverse: a ^ a = 0 and a ^ 0 = a. However, this fails if both variables point to the same memory location.
Q2: How can you check if a number is a power of 2 using bitwise operators?
Use n & (n - 1) == 0 (for n > 0). Powers of 2 have exactly one bit set (e.g., 8 = 1000). Subtracting 1 flips all bits below and including that bit (7 = 0111). AND-ing them gives 0 only for powers of 2.
Q3: What is the difference between >> for signed and unsigned integers?
For unsigned integers, right shift always fills with 0 (logical shift). For signed integers, the behavior is implementation-defined — most compilers use arithmetic shift (fills with the sign bit), so negative numbers remain negative after right shift.
Q4: How do you count the number of set bits in an integer efficiently?
Brian Kernighan's algorithm: repeatedly do n = n & (n-1) which clears the lowest set bit each time. Count iterations until n becomes 0. This runs in O(number of set bits) time, which is optimal.
Q5: What's the difference between ~ and ! in C?
~ is bitwise NOT — it inverts every bit of the number (~5 = -6 for signed int). ! is logical NOT — it returns 1 if the operand is 0, and 0 for any non-zero value (!5 = 0). They serve completely different purposes.
Summary
- Bitwise operators work on individual bits:
&,|,^,~,<<,>> - Left shift multiplies by powers of 2; right shift divides by powers of 2
- AND is used for masking and clearing bits; OR is used for setting bits
- XOR is useful for toggling and has the property that
a ^ a = 0 n & (n-1)clears the lowest set bit — the basis for many bit tricks- Always use unsigned types for predictable bitwise behavior
- Bit manipulation is crucial for embedded systems, networking, and performance-critical code
Exam Focus
Revise definitions, diagrams, examples, and short-answer points for Bitwise Operators in C Programming.
Interview Use
Prepare one clear explanation, one practical example, and one common mistake for this C Programming topic.
Search Terms
c-programming, c programming, programming, operators, bitwise, bitwise operators in c programming
Related C Programming Topics