C: 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.

1. Binary Review

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

TEXT 📖 Display only
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.

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

2. 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)

(1) Bitwise AND (&)

TEXT 📖 Display only
  1010  (10)
& 1100  (12)
------
  1000  (8)

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

(2) Bitwise OR (|)

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

Uses: setting certain bits to 1.

(3) Bitwise XOR (^)

TEXT 📖 Display only
  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.

(4) Bitwise NOT (~)

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

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

(5) Left Shift (<<)

TEXT 📖 Display only
5 << 2
= 00000101 << 2
= 00010100
= 20

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

(6) Right Shift (>>)

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


3. Four Essential Bit Operations

(1) Set a Bit (to 1)

TEXT 📖 Display only
flags |= (1 << n);

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

(2) 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.

(3) Toggle a Bit

TEXT 📖 Display only
flags ^= (1 << n);

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

(4) 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 📖 Display only
Set bit 3: 00001000
Set bit 5: 00101000
Clear bit 3: 00100000
Toggle bit 5: 00000000
Toggle bit 7: 10000000
Bit 7 is 1

4. Mask Techniques

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

(1) 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.

(2) Extract High Bits

TEXT 📖 Display only
unsigned int high_byte = (val >> 8) & 0xFF;

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

(3) 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 📖 Display only
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.


5. Permission Flags

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

C
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 📖 Display only
Permissions: rwxr-xr-x
After removing write: r-xr-xr-x

6. Practical Bit Tricks

(1) 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.

(2) Check Odd or Even

TEXT 📖 Display only
if (n & 1) {
}

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

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

(4) Compute Powers of 2

TEXT 📖 Display only
unsigned int pow2 = 1u << n;

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

(5) 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 📖 Display only
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 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

📝 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)
Web-Tutorial.com

Web-Tutorial Tech Team

A team of developers maintaining programming tutorials. Each tutorial is written and reviewed by developers with expertise in that field. We work to keep our content accurate and reliable — if you spot an issue, please let us know.

100%

🙏 帮我们做得更好

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

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