Bitwise Operators

Bitwise operators work on the individual bits of an integer. They're how C programs pack several on/off flags into one number, talk to hardware registers, and implement fast arithmetic tricks — everyday tools in systems and embedded programming.

Binary in a nutshell

Each bit of an integer is a power of two. The 8-bit value 0b00101101 is 32 + 8 + 4 + 1 = 45. Hexadecimal is a compact way to write bits — each hex digit is exactly four bits, so 0x2D is 0010 1101.

A helper to print an 8-bit value in binary will make the examples clearer:

C
#include <stdio.h>
#include <stdint.h>

void print_bits(uint8_t value) {
    for (int bit = 7; bit >= 0; bit--) {
        putchar((value >> bit) & 1 ? '1' : '0');
    }
    putchar('\n');
}

int main(void) {
    print_bits(45);
    print_bits(0x2D);
    print_bits(255);
    return 0;
}
Output
00101101
00101101
11111111

The operators

OperatorNameEach result bit is 1 when…
a & bANDboth bits are 1
a | bOReither bit is 1
a ^ bXORthe bits differ
~aNOTthe bit was 0
a << nleft shiftbits move n places left, zeros fill in
a >> nright shiftbits move n places right
C
#include <stdio.h>
#include <stdint.h>

void print_bits(uint8_t value) {
    for (int bit = 7; bit >= 0; bit--) putchar((value >> bit) & 1 ? '1' : '0');
    putchar('\n');
}

int main(void) {
    uint8_t a = 0xCA, b = 0x0F;   // 11001010 and 00001111
    print_bits(a & b);
    print_bits(a | b);
    print_bits(a ^ b);
    print_bits((uint8_t) ~a);
    print_bits((uint8_t) (a << 2));
    print_bits(a >> 4);
    return 0;
}
Output
00001010
11001111
11000101
00110101
00101000
00001100

Don't confuse & with && or | with ||: the logical operators treat a whole value as true/false, the bitwise ones work bit by bit.

Flags in one integer

Give each option its own bit, then combine them with |. This is how many C APIs take options — open() flags in POSIX are a familiar example.

C
#include <stdio.h>

enum {
    PERM_READ    = 1u << 0,   // 0001
    PERM_WRITE   = 1u << 1,   // 0010
    PERM_EXECUTE = 1u << 2,   // 0100
    PERM_ADMIN   = 1u << 3    // 1000
};

int main(void) {
    unsigned perms = PERM_READ | PERM_WRITE;          // set two flags

    printf("can write?  %s\n", (perms & PERM_WRITE) ? "yes" : "no");
    printf("can execute? %s\n", (perms & PERM_EXECUTE) ? "yes" : "no");

    perms |= PERM_EXECUTE;                            // turn a flag on
    perms &= ~PERM_WRITE;                             // turn a flag off
    perms ^= PERM_ADMIN;                              // toggle a flag

    printf("perms = %u (read %d, write %d, exec %d, admin %d)\n", perms,
           !!(perms & PERM_READ), !!(perms & PERM_WRITE),
           !!(perms & PERM_EXECUTE), !!(perms & PERM_ADMIN));
    return 0;
}
Output
can write?  yes
can execute? no
perms = 13 (read 1, write 0, exec 1, admin 1)
TaskIdiom
Set a bitx |= FLAG;
Clear a bitx &= ~FLAG;
Toggle a bitx ^= FLAG;
Test a bitif (x & FLAG)

!! turns any non-zero value into exactly 1.

Masks and fields

Several small values can share one integer. An RGB colour packs red, green and blue into 24 bits; shifts and masks pull them apart:

C
#include <stdio.h>
#include <stdint.h>

int main(void) {
    uint32_t colour = 0x3A7BD5;
    unsigned red   = (colour >> 16) & 0xFF;
    unsigned green = (colour >> 8) & 0xFF;
    unsigned blue  = colour & 0xFF;
    printf("rgb(%u, %u, %u)\n", red, green, blue);

    uint32_t rebuilt = (red << 16) | (green << 8) | blue;
    printf("#%06X\n", (unsigned) rebuilt);
    return 0;
}
Output
rgb(58, 123, 213)
#3A7BD5

Shifts as multiplication

Shifting left by n multiplies by 2n, and shifting an unsigned value right by n divides by 2n. Modern compilers already do this optimisation, so write * 8 when you mean multiplication and save shifts for bit work.

Rules to stay safe

  • Use unsigned types for bit manipulation. Shifting a negative number left, or a 1 into the sign bit, is undefined behaviour; right-shifting a negative number is implementation-defined.
  • Don't shift by the width or more — x << 32 on a 32-bit value is undefined.
  • Parenthesise. &, ^ and | have lower precedence than ==.

Try it yourself

Count how many bits are set in a number by repeatedly testing the lowest bit and shifting right. (A neat trick: n &= n - 1 clears the lowest set bit.)

Try it yourself · main.c
Edit the code, copy it into main.c, then compile and run it with cc -std=c17 -Wall main.c -o main && ./main. How to set up a C compiler
Output
0xF0F1 has 9 bits set