1. Introduction
Bitwise operators act directly on the individual bits of integer operands, rather than treating the operand as a whole number. They are essential for low-level programming tasks such as setting or clearing flags, packing data, working with hardware registers, and writing efficient masks — and they are a frequent source of exam questions and interview trick questions.
Cricket analogy: DRS checks multiple micro-signals (snickometer, hotspot, ball-tracking) individually rather than the whole delivery, like bitwise ops inspecting individual bits instead of the whole number.
C provides six bitwise operators: bitwise AND (&), bitwise OR (|), bitwise XOR (^), bitwise NOT / one's complement (~), left shift (<<), and right shift (>>). They only operate on integer types (char, short, int, long, etc.) — never on float or double.
Cricket analogy: Sachin Tendulkar's toolkit had six signature shots like the cover drive and pull, just as C has six bitwise operators, and neither works on a wide-ball call the way bitwise ops refuse float operands.
2. Syntax
a & b // bitwise AND — binary operator
a | b // bitwise OR — binary operator
a ^ b // bitwise XOR — binary operator
~a // bitwise NOT (one's complement) — unary operator
a << n // left shift a by n bit positions
a >> n // right shift a by n bit positions3. Explanation
Bitwise AND (&) sets each result bit to 1 only if both corresponding bits are 1. Truth table for a single bit: 0&0=0, 0&1=0, 1&0=0, 1&1=1. AND is commonly used with a mask to clear or check specific bits, e.g. flags & 0x01 checks whether bit 0 is set.
Cricket analogy: Bitwise AND is like checking if a fielder is both inside the 30-yard circle AND is a designated boundary rider, the condition only holds when both are simultaneously true, like flags & 0x01.
Bitwise OR (|) sets each result bit to 1 if at least one corresponding bit is 1. Truth table: 0|0=0, 0|1=1, 1|0=1, 1|1=1. OR is used to set specific bits, e.g. flags = flags | 0x04; sets bit 2 without disturbing other bits.
Cricket analogy: Setting a bit with OR is like adding a specialist spinner to Virat Kohli's XI without dropping any existing bowler, you add one flag without disturbing the rest of the team.
Bitwise XOR (^) sets each result bit to 1 if exactly one of the corresponding bits is 1 (the bits differ). Truth table: 0^0=0, 0^1=1, 1^0=1, 1^1=0. XOR is used to toggle bits (flags ^= mask flips the masked bits) and has the property that x ^ x == 0 and x ^ 0 == x, which enables the classic (but not recommended in production code) swap-without-temp trick.
Cricket analogy: XOR is like a scoreboard operator toggling the power-play-active indicator on and off each over, flip it once it's on, flip it again it's back to off, echoing x ^ x == 0.
Bitwise NOT (~), also called one's complement, is a unary operator that inverts every bit of its operand: each 0 becomes 1 and each 1 becomes 0. For an 8-bit value, ~0000 0101 (5) becomes 1111 1010. On a typical two's-complement system, ~x is equivalent to -x - 1, so ~0 equals -1 for a signed int.
Cricket analogy: Flipping every bit is like inverting a win/loss ledger for a franchise, every win becomes a loss and vice versa, the same way ~x inverts every 0 to 1 and every 1 to 0.
Left shift (a << n) shifts all bits of a left by n positions, filling the vacated low-order bits with 0. Each left shift by 1 is equivalent to multiplying by 2 (as long as no significant bit overflows out of the type's range); a << n is equivalent to a * 2^n for non-negative, non-overflowing values.
Cricket analogy: Left-shifting a run total is like a batsman's score doubling every over in a hypothetical exhibition format, shift left by 1 doubles the value, just as a << 1 equals a * 2.
Right shift (a >> n) shifts all bits right by n positions. For UNSIGNED integers, C guarantees a logical shift: vacated high-order bits are always filled with 0. For SIGNED integers, the C standard leaves the behaviour of right-shifting a negative value implementation-defined — in practice, virtually all mainstream compilers (GCC, Clang, MSVC) perform an arithmetic shift, filling vacated high-order bits with copies of the sign bit, so the sign is preserved. Right-shifting a non-negative signed value by n is equivalent to integer division by 2^n.
Cricket analogy: Halving a run rate target when rain reduces overs, Duckworth-Lewis-style, resembles right shift dividing by 2^n, with the target's sign always preserved like an arithmetic shift.
Bitwise operators have LOWER precedence than relational operators (==, !=, <, >, etc.). This means if (a & mask == 1) is parsed as if (a & (mask == 1)), almost never what a beginner intends. Always parenthesize explicitly: if ((a & mask) == 1).
Shifting by a negative amount, or by an amount greater than or equal to the width in bits of the promoted operand (e.g. 1 << 32 for a 32-bit int), is undefined behaviour in C. Left-shifting a signed value such that a 1 bit is shifted into or past the sign bit is also undefined behaviour. Always keep shift counts within 0 to (bit-width - 1).
4. Example
#include <stdio.h>
int main(void) {
unsigned int a = 12; // 0000 1100
unsigned int b = 10; // 0000 1010
printf("a & b = %u\n", a & b); // 0000 1000 -> 8
printf("a | b = %u\n", a | b); // 0000 1110 -> 14
printf("a ^ b = %u\n", a ^ b); // 0000 0110 -> 6
printf("~a = %d\n", ~a); // one's complement of 12 (as int)
printf("a << 2 = %u\n", a << 2); // 0011 0000 -> 48 (12 * 4)
printf("a >> 2 = %u\n", a >> 2); // 0000 0011 -> 3 (12 / 4)
int neg = -8; // signed, negative
printf("neg >> 1 = %d\n", neg >> 1); // implementation-defined; -4 on GCC/Clang (arithmetic shift)
unsigned int flags = 0;
flags |= (1u << 2); // set bit 2
flags |= (1u << 0); // set bit 0
printf("flags = %u\n", flags); // 5
flags &= ~(1u << 0); // clear bit 0
printf("flags after clear = %u\n", flags); // 4
return 0;
}5. Output
a & b = 8
a | b = 14
a ^ b = 6
~a = -13
a << 2 = 48
a >> 2 = 3
neg >> 1 = -4
flags = 5
flags after clear = 46. Key Takeaways
- & sets a bit only if both bits are 1; | sets a bit if either bit is 1; ^ sets a bit if the two bits differ.
- ~ (one's complement) flips every bit; on two's-complement systems ~x equals -x - 1, so ~0 is -1.
- << shifts bits left and fills with 0s, equivalent to multiplying by a power of 2 when no overflow occurs.
- >> on unsigned values is always a logical shift (fills with 0); on signed negative values it is implementation-defined, but is an arithmetic (sign-preserving) shift on virtually all common compilers.
- Bitwise operators bind looser than relational operators, so expressions like
(a & mask) == valuealways need explicit parentheses. - Shifting by a negative count or by ≥ the bit-width of the type is undefined behaviour — never do it.
Practice what you learned
1. What is the result of `6 & 3` in binary?
2. What does `~0` evaluate to for a signed int on a typical two's-complement system?
3. For an unsigned int `x`, what does `x >> 1` compute?
4. Why does `if (flags & MASK == 1)` usually not do what a beginner expects?
5. What is the outcome of right-shifting a negative signed int, e.g. `-8 >> 1`, according to the C standard?
Was this page helpful?
You May Also Like
Operators in C
Learn C operators — arithmetic, relational, logical, assignment, and increment/decrement — with syntax, examples, and exam-ready MCQs.
Data Types in C
Master C data types — int, float, char, double, and modifiers — with sizes, ranges, format specifiers, and examples.
Common C Interview Questions
Top C programming interview questions and answers covering pointers, memory allocation, storage classes, structures, and const/volatile.
Related Reading
Related Study Notes in Programming
Browse all study notesApache Spark Study Notes
Programming · 30 topics
ProgrammingApache Flink Study Notes
Programming · 30 topics
ProgrammingHadoop Study Notes
Programming · 30 topics
ProgrammingSnowflake Study Notes
Programming · 30 topics
ProgrammingApache Airflow Study Notes
Programming · 30 topics
Programmingdbt (Data Build Tool) Study Notes
Programming · 30 topics