100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
C

Bitwise Operators in C

Master C bitwise operators — AND, OR, XOR, NOT, left shift, right shift — with truth tables, precedence, and signed-shift pitfalls.

Operators & Control FlowBeginner11 min readJul 7, 2026
Analogies

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

c
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 positions

3. 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

c
#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

text
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 = 4

6. 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) == value always 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

Was this page helpful?

Topics covered

#CProgrammingStudyNotes#Programming#BitwiseOperatorsInC#Bitwise#Operators#Syntax#Explanation#StudyNotes#SkillVeris#ExamPrep