Bitwise Operations

Bitwise operations are like controlling a row of light switches — each light corresponds to one bit, and you can turn on, turn off, or flip any individual light without affecting the others.

Binary Review

Computers represent all data internally using binary. One byte = 8 bits, each bit is 0 or 1.

Decimal 42  =  Binary 00101010
Decimal 255 =  Binary 11111111

Signed integers use two's complement representation: the highest bit is the sign bit, 0 for positive, 1 for negative.

Decimal  5  =  00000101
Decimal -5  =  11111011  (two's complement)

Six Bitwise Operators

Operator Name Rule
& Bitwise AND Result is 1 only when both bits are 1
| Bitwise OR Result is 1 when at least one bit is 1
^ Bitwise XOR Result is 1 when the two bits differ
~ Bitwise NOT 0 becomes 1, 1 becomes 0
<< Left shift Shifts bits left, fills low bits with 0
>> Right shift Shifts bits right, fills high bits with sign bit (signed) or 0 (unsigned)

Bitwise AND (&)

  1010  (10)
& 1100  (12)
------
  1000  (8)

Uses: clearing certain bits, checking whether certain bits are 1.

Bitwise OR (|)

  1010  (10)
| 1100  (12)
------
  1110  (14)

Uses: setting certain bits to 1.

Bitwise XOR (^)

  1010  (10)
^ 1100  (12)
------
  0110  (6)

Properties: a ^ a = 0, a ^ 0 = a. XOR is reversible — XORing twice with the same value restores the original.

Bitwise NOT (~)

~ 00101010  (42)
= 11010101  (-43, two's complement)

For unsigned numbers: ~0 = 255 (8-bit), ~0 = 0xFFFFFFFF (32-bit).

Left Shift (<<)

5 << 2
= 00000101 << 2
= 00010100
= 20

Left shifting by n is equivalent to multiplying by 2^n. 5 << 2 = 5 * 4 = 20.

Right Shift (>>)

20 >> 2
= 00010100 >> 2
= 00000101
= 5

Right shifting by n is equivalent to dividing by 2^n (rounded toward zero).

⚠️ Note: Right-shifting signed negative numbers fills high bits with 1 (arithmetic shift), which may produce unexpected results. It's recommended to only shift unsigned values.

Four Essential Bit Operations

Set a Bit (to 1)

C
flags |= (1 << n);

Sets the nth bit to 1, leaving all other bits unchanged.

Clear a Bit (to 0)

C
flags &= ~(1 << n);

~(1 << n) produces a mask that is all 1s except for a 0 at the nth position. ANDing clears only that bit.

Toggle a Bit

C
flags ^= (1 << n);

XOR operation: 0^1=1, 1^1=0, which perfectly implements toggling.

Check a Bit

C
if (flags & (1 << n)) {
}

If the nth bit is 1, the result is non-zero; if 0, the result is zero.

Example

C
#include <stdio.h>

void print_bits(unsigned char val) {
    for (int i = 7; i >= 0; i--) {
        printf("%d", (val >> i) & 1);
    }
    printf("\n");
}

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

    flags |= (1 << 3);
    printf("Set bit 3: ");
    print_bits(flags);

    flags |= (1 << 5);
    printf("Set bit 5: ");
    print_bits(flags);

    flags &= ~(1 << 3);
    printf("Clear bit 3: ");
    print_bits(flags);

    flags ^= (1 << 5);
    printf("Toggle bit 5: ");
    print_bits(flags);

    flags ^= (1 << 7);
    printf("Toggle bit 7: ");
    print_bits(flags);

    if (flags & (1 << 7)) {
        printf("Bit 7 is 1\n");
    }

    return 0;
}
▶ Try it Yourself
TEXT
Set bit 3: 00001000
Set bit 5: 00101000
Clear bit 3: 00100000
Toggle bit 5: 00000000
Toggle bit 7: 10000000
Bit 7 is 1

Mask Techniques

A mask is a predefined combination of bits used to extract or modify specific bit fields within data.

Extract Low Bits

C
unsigned int val = 0xABCD;
unsigned int low_byte = val & 0xFF;

0xFF is a mask that keeps only the lowest 8 bits.

Extract High Bits

C
unsigned int high_byte = (val >> 8) & 0xFF;

First shift right by 8 bits, then mask to get the low 8 bits.

Combine Values

C
unsigned int combined = (high << 8) | low;

Join two bytes into a 16-bit value.

Example

RGB color value extraction. A 24-bit color value has 8 bits each for red, green, and blue:

C
#include <stdio.h>

int main(void) {
    unsigned int color = 0xFF6633;

    unsigned char r = (color >> 16) & 0xFF;
    unsigned char g = (color >> 8) & 0xFF;
    unsigned char b = color & 0xFF;

    printf("Color #FF6633:\n");
    printf("  Red: %d\n", r);
    printf("  Green: %d\n", g);
    printf("  Blue: %d\n", b);

    unsigned int new_color = 0x00;
    new_color |= ((r / 2) << 16);
    new_color |= ((g / 2) << 8);
    new_color |= (b / 2);
    printf("Darkened: #%06X\n", new_color);

    return 0;
}
▶ Try it Yourself
TEXT
Color #FF6633:
  Red: 255
  Green: 102
  Blue: 51
Darkened: #7F3319
💡 Tip: RGB color extraction is a classic application of bitwise operations. In web development, the #RRGGBB format is essentially a 24-bit integer — red in bits 16-23, green in bits 8-15, blue in bits 0-7.

Permission Flags

Linux file permissions are a classic application of bitwise operations. 9 permission bits represent owner/group/other for read/write/execute:

rwxr-xr-x = 111101101 = 0755
rw-r--r-- = 110100100 = 0644
C
#include <stdio.h>

#define READ    (1 << 2)
#define WRITE   (1 << 1)
#define EXECUTE (1 << 0)

void show_permission(unsigned char perm) {
    printf("%c", (perm & READ) ? 'r' : '-');
    printf("%c", (perm & WRITE) ? 'w' : '-');
    printf("%c", (perm & EXECUTE) ? 'x' : '-');
}

int main(void) {
    unsigned char owner  = READ | WRITE | EXECUTE;
    unsigned char group  = READ | EXECUTE;
    unsigned char other  = READ | EXECUTE;

    printf("Permissions: ");
    show_permission(owner);
    show_permission(group);
    show_permission(other);
    printf("\n");

    owner &= ~WRITE;
    printf("After removing write: ");
    show_permission(owner);
    show_permission(group);
    show_permission(other);
    printf("\n");

    return 0;
}
TEXT
Permissions: rwxr-xr-x
After removing write: r-xr-xr-x

Practical Bit Tricks

Swap Two Variables (Without a Temporary)

C
a ^= b;
b ^= a;
a ^= b;

Principle: the self-inverse property of XOR. However, this hurts readability and is not recommended in real development.

Check Odd or Even

C
if (n & 1) {
}

If the lowest bit is 1, the number is odd. This is faster than n % 2.

Multiply/Divide by Powers of 2

C
n << 1
n << 2
n >> 1

Compilers typically optimize n * 2 into a shift, but shifts are only safe for positive integers.

Compute Powers of 2

C
unsigned int pow2 = 1u << n;

1u << 0 = 1, 1u << 1 = 2, 1u << 8 = 256... Shifting is much faster than pow(2, n).

Align to a Power of 2

C
unsigned int aligned = (value + mask) & ~mask;

For example, align to a 4-byte boundary: (n + 3) & ~3.

C
#include <stdio.h>

int main(void) {
    int values[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    for (int i = 0; i < 10; i++) {
        int aligned = (values[i] + 3) & ~3;
        printf("%d -> %d\n", values[i], aligned);
    }
    return 0;
}
TEXT
0 -> 0
1 -> 4
2 -> 4
3 -> 4
4 -> 4
5 -> 8
6 -> 8
7 -> 8
8 -> 8
9 -> 12

❓ FAQ

Q Why are shift operations faster than multiplication and division?
A Shifts are single-cycle CPU instructions, while multiplication and division require multiple clock cycles. However, modern compilers automatically optimize multiplication by powers of 2 into shifts, so there's no need to write them manually.
Q What happens if you shift beyond the number of bits? *A: Undefined behavior! Shifting a 32-bit int by 32 or more bits is not allowed. You must ensure the shift count is between 0 and sizeof(type)8-1.

Q: Can bitwise operations be used on floating-point numbers?

A No. Bitwise operators only work on integer types. To manipulate the bits of a floating-point number, you need to copy its bytes to an integer using memcpy.
Q What's wrong with the XOR swap?
A If a and b point to the same memory (e.g., the same variable), the XOR swap will zero out the value. Plus, it's hard to read, and the compiler optimizes a regular swap to be just as efficient.

📖 Summary

  • Six bitwise operators: &, |, ^, ~, <<, >>
  • Set a bit with |=, clear with &= ~, toggle with ^=, check with &
  • Masks are the core technique of bitwise operations, used to extract and combine bit fields
  • Permission flags are combined with OR, checked with AND, and cleared with NOT
  • Left shift multiplies by 2, right shift divides by 2, align with (n + mask) & ~mask

📝 Exercises

  1. Write a function that returns the number of 1-bits in an integer's binary representation (using bitwise operations, not dividing by 2 in a loop)
  2. Write a program that uses bitwise operations to adjust the brightness of an RGB color (multiply each channel by a coefficient and recombine)
  3. Write a function that extracts bits m through n from a 32-bit integer (m < n, counting from bit 0)
100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏