CBitwise Operators

Bitwise Operators

Bitwise operators work directly on the individual binary digits (bits) that make up an integer's representation in memory. They are essential for low-level programming: device drivers, network protocols, embedded systems, and any code that packs multiple flags into a single value.

Operator

Name

Behavior

&

AND

Result bit is 1 only if both input bits are 1

|

OR

Result bit is 1 if either input bit is 1

^

XOR

Result bit is 1 if the input bits differ

~

NOT

Flips every bit (unary operator)

<<

Left shift

Shifts bits left, filling with zeros

>>

Right shift

Shifts bits right

C
#include <stdio.h>

int main(void) {
    unsigned int a = 12; // binary: 1100
    unsigned int b = 10; // binary: 1010

    printf("%u\n", a & b);  // 8  (1000)
    printf("%u\n", a | b);  // 14 (1110)
    printf("%u\n", a ^ b);  // 6  (0110)
    printf("%u\n", ~a);     // implementation-defined bit pattern, all bits flipped
    printf("%u\n", a << 1); // 24 (1100 becomes 11000)
    printf("%u\n", a >> 1); // 6  (1100 becomes 0110)
    return 0;
}
Shifting as Fast Multiply/Divide by Powers of Two
Shifting left by n is equivalent to multiplying by 2^n, and shifting right by n is equivalent to dividing by 2^n (for non-negative values). Compilers already perform this optimization automatically when it is safe to do so, but understanding the relationship helps when reading low-level code.

C
int value = 5;
printf("%d\n", value << 2); // 20, same as 5 * 4
printf("%d\n", value >> 1); // 2,  same as 5 / 2 (integer division)
Practical Use: Bit Flags for Settings

A very common pattern is packing several independent on/off options into a single byte or integer, where each bit represents one option. This saves memory and lets you test, set, or clear flags with a single bitwise operation instead of several boolean variables.

C
#include <stdio.h>

#define PERM_READ    (1 << 0) // 0001
#define PERM_WRITE   (1 << 1) // 0010
#define PERM_EXECUTE (1 << 2) // 0100
#define PERM_DELETE  (1 << 3) // 1000

int main(void) {
    unsigned char permissions = 0;

    permissions |= PERM_READ;   // set the READ flag
    permissions |= PERM_WRITE;  // set the WRITE flag

    if (permissions & PERM_WRITE) {
        printf("write access granted\n");
    }

    permissions &= ~PERM_WRITE; // clear the WRITE flag, keep the rest
    if (!(permissions & PERM_WRITE)) {
        printf("write access revoked\n");
    }

    permissions ^= PERM_EXECUTE; // toggle EXECUTE
    printf("final permissions: %d\n", permissions);
    return 0;
}
Note
The idiom flags &= ~SOME_FLAG; is the standard way to clear a single bit: ~SOME_FLAG produces a mask with every bit set except the one you want to clear, and AND-ing with it leaves all other bits untouched.
Right-Shifting Negative Numbers
Right shift of a negative signed value is implementation-defined
Left-shifting is well defined for non-negative values. But right- shifting a negative signed integer is implementation- defined behavior in C: most compilers perform an arithmetic shift (copying the sign bit into the vacated high bits, so the value stays negative), but the standard does not guarantee this. If you need predictable bit manipulation, perform shifts on unsigned types, where the behavior is always a well-defined logical shift that fills with zeros.

C
int negative = -8;
printf("%d\n", negative >> 1); // typically -4, but not guaranteed by the standard

unsigned int positive = 8u;
printf("%u\n", positive >> 1); // always 4 — well defined for unsigned types
  • &, |, ^, ~, <<, >> operate on the binary representation of integers

  • Left shift multiplies by a power of two; right shift divides (for non-negative values)

  • Bit flags let you pack multiple boolean options into one integer using OR to set, AND-NOT to clear, and AND to test

  • Prefer unsigned types for shifting to avoid implementation-defined behavior on negative numbers