Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Bit Manipulation

C is a language that operates very close to the hardware. All data in your computer–integers, characters, etc.–is ultimately stored as a sequence of BITS (binary digits), which are either 0 or 1.

C provides a special set of BITWISE OPERATORS that allow you to manipulate these individual bits directly. This is a low-level feature that is incredibly powerful for tasks like:

  • Controlling hardware devices where each bit in a register has a meaning.
  • Writing highly efficient code for specific algorithms.
  • Storing multiple boolean (true/false) flags in a single byte.

The Bitwise Operators

& (Bitwise AND): Sets a bit to 1 if BOTH corresponding bits are 1. | (Bitwise OR): Sets a bit to 1 if EITHER corresponding bit is 1. ^ (Bitwise XOR): Sets a bit to 1 if the corresponding bits are DIFFERENT. ~ (Bitwise NOT): Inverts all the bits (0s become 1s and 1s become 0s). << (Left Shift): Shifts all bits to the left by a specified number of places.

(Right Shift): Shifts all bits to the right by a specified number of places.

Full Source

/**
 * @file 21_bit_manipulation.c
 * @brief Part 3, Lesson 21: Bit Manipulation
 * @author dunamismax
 * @date 06-15-2025
 *
 * This file serves as the lesson and demonstration for bit manipulation. It
 * introduces the bitwise operators and shows how to directly manipulate the
 * individual bits of data, a powerful technique for optimization and control.
 */

/*
 * =====================================================================================
 * |                                   - LESSON START -                                  |
 * =====================================================================================
 *
 * C is a language that operates very close to the hardware. All data in your
 * computer--integers, characters, etc.--is ultimately stored as a sequence of
 * BITS (binary digits), which are either 0 or 1.
 *
 * C provides a special set of BITWISE OPERATORS that allow you to manipulate
 * these individual bits directly. This is a low-level feature that is incredibly
 * powerful for tasks like:
 * - Controlling hardware devices where each bit in a register has a meaning.
 * - Writing highly efficient code for specific algorithms.
 * - Storing multiple boolean (true/false) flags in a single byte.
 *
 * --- The Bitwise Operators ---
 *
 * &  (Bitwise AND):     Sets a bit to 1 if BOTH corresponding bits are 1.
 * |  (Bitwise OR):      Sets a bit to 1 if EITHER corresponding bit is 1.
 * ^  (Bitwise XOR):     Sets a bit to 1 if the corresponding bits are DIFFERENT.
 * ~  (Bitwise NOT):     Inverts all the bits (0s become 1s and 1s become 0s).
 * << (Left Shift):      Shifts all bits to the left by a specified number of places.
 * >> (Right Shift):     Shifts all bits to the right by a specified number of places.
 */

#include <stdio.h>
#include <stdint.h> // For uint8_t, an unsigned 8-bit integer type.

// --- A Helper Function to Visualize Bits ---
// To understand bit manipulation, you need to SEE the bits. This function
// will print the binary representation of a byte (an 8-bit number).
// We use `uint8_t` for a clear, 8-bit unsigned integer.
void print_binary(uint8_t byte)
{
    printf("0b"); // "0b" prefix indicates a binary number
    // We loop from the most significant bit (7) down to the least (0).
    for (int i = 7; i >= 0; i--)
    {
        // `(byte >> i)` shifts the bit we're interested in to the 0th position.
        // `& 1` then isolates that bit. If it's 1, the result is 1; otherwise, 0.
        printf("%d", (byte >> i) & 1);
    }
}

int main(void)
{
    // Let's use two small numbers for our demonstration.
    uint8_t a = 5; // Binary: 00000101
    uint8_t b = 9; // Binary: 00001001

    printf("--- Part 1: Basic Bitwise Operations ---\n");
    printf("a = %2d = ", a);
    print_binary(a);
    printf("\n");
    printf("b = %2d = ", b);
    print_binary(b);
    printf("\n\n");

    // Bitwise AND
    uint8_t result_and = a & b; // 00000101 & 00001001 = 00000001 (1)
    printf("a & b = %2d = ", result_and);
    print_binary(result_and);
    printf("\n");

    // Bitwise OR
    uint8_t result_or = a | b; // 00000101 | 00001001 = 00001101 (13)
    printf("a | b = %2d = ", result_or);
    print_binary(result_or);
    printf("\n");

    // Bitwise XOR
    uint8_t result_xor = a ^ b; // 00000101 ^ 00001001 = 00001100 (12)
    printf("a ^ b = %2d = ", result_xor);
    print_binary(result_xor);
    printf("\n");

    // Bitwise NOT
    uint8_t result_not_a = ~a; // ~00000101 = 11111010 (250)
    printf("  ~a  = %3d = ", result_not_a);
    print_binary(result_not_a);
    printf("\n\n");

    // --- Part 2: Shift Operations ---
    printf("--- Part 2: Shift Operations ---\n");
    uint8_t c = 12; // Binary: 00001100

    printf("c = %2d = ", c);
    print_binary(c);
    printf("\n");

    // Left Shift (equivalent to multiplying by 2^n)
    uint8_t left_shifted = c << 2; // 12 * 2^2 = 12 * 4 = 48 (00110000)
    printf("c << 2 = %2d = ", left_shifted);
    print_binary(left_shifted);
    printf("\n");

    // Right Shift (equivalent to integer division by 2^n)
    uint8_t right_shifted = c >> 1; // 12 / 2^1 = 12 / 2 = 6 (00000110)
    printf("c >> 1 = %2d = ", right_shifted);
    print_binary(right_shifted);
    printf("\n\n");

    // --- Part 3: Practical Use - BITMASKS for Flags ---
    // A very common use case is to store multiple on/off states in a single byte.
    // Each bit position represents a different flag.

    printf("--- Part 3: Practical Bitmasking ---\n");
    const uint8_t FLAG_READ_ACCESS = 1 << 0;    // 00000001 (1)
    const uint8_t FLAG_WRITE_ACCESS = 1 << 1;   // 00000010 (2)
    const uint8_t FLAG_EXECUTE_ACCESS = 1 << 2; // 00000100 (4)
    const uint8_t FLAG_IS_ADMIN = 1 << 7;       // 10000000 (128)

    uint8_t user_permissions = 0; // Start with no permissions.
    printf("Initial permissions: ");
    print_binary(user_permissions);
    printf("\n");

    // 1. SETTING a bit (giving permission) using OR
    user_permissions = user_permissions | FLAG_READ_ACCESS;
    user_permissions = user_permissions | FLAG_WRITE_ACCESS;
    printf("After giving R/W:    ");
    print_binary(user_permissions);
    printf("\n");

    // 2. CHECKING a bit (do they have permission?) using AND
    if (user_permissions & FLAG_WRITE_ACCESS)
    {
        printf("User has WRITE access.\n");
    }
    if (!(user_permissions & FLAG_EXECUTE_ACCESS))
    {
        printf("User does NOT have EXECUTE access.\n");
    }
    if (!(user_permissions & FLAG_IS_ADMIN))
    {
        printf("User is NOT an administrator.\n");
    }

    // 3. CLEARING a bit (revoking permission) using AND with NOT
    user_permissions = user_permissions & ~FLAG_WRITE_ACCESS;
    printf("After revoking W:    ");
    print_binary(user_permissions);
    printf("\n");

    // 4. TOGGLING a bit (flipping its state) using XOR
    user_permissions = user_permissions ^ FLAG_READ_ACCESS; // Turn it off
    user_permissions = user_permissions ^ FLAG_READ_ACCESS; // Turn it back on
    printf("After toggling R twice:");
    print_binary(user_permissions);
    printf("\n");

    return 0;
}

/*
 * =====================================================================================
 * |                                    - LESSON END -                                   |
 * =====================================================================================
 *
 * Key Takeaways:
 *
 * 1.  Bitwise operators (`&`, `|`, `^`, `~`, `<<`, `>>`) allow you to directly
 *     manipulate the 0s and 1s that make up numeric data types.
 * 2.  A BITMASK is a value (often a constant) used to target specific bits in another
 *     variable for setting, clearing, or checking.
 * 3.  The common patterns for bitmasking are:
 *     - Set bit:      `flags = flags | MASK;`
 *     - Clear bit:    `flags = flags & ~MASK;`
 *     - Check bit:    `if (flags & MASK) { ... }`
 *     - Toggle bit:   `flags = flags ^ MASK;`
 * 4.  Shift operators provide a very fast way to perform multiplication and
 *     division by powers of two.
 *
 * This is a low-level but essential skill for any serious C programmer.
 *
 * HOW TO COMPILE AND RUN THIS CODE:
 *
 * 1. Open a terminal or command prompt.
 * 2. Navigate to the directory where you saved this file.
 * 3. Use the GCC compiler to create an executable file:
 *    `gcc -Wall -Wextra -std=c11 -o 21_bit_manipulation 21_bit_manipulation.c`
 * 4. Run the executable:
 *    - On Linux/macOS:   `./21_bit_manipulation`
 *    - On Windows:       `21_bit_manipulation.exe`
 */

How to Compile and Run

cc -Wall -Wextra -std=c11 -o 21_bit_manipulation 21_bit_manipulation.c
./21_bit_manipulation