C++ Bitwise Operations Fundamentals: Bitwise Extraction and Flag Setting

In actual C++ development, bitwise operations are a common technique, especially when dealing with system states, flags, or control bits. Bitwise operations can provide very efficient solutions. This article will illustrate how to use bitwise operations to retrieve and set specific flags through an example.

Bitwise Operations Fundamentals

In computers, data is stored in binary bits (0 and 1). Bitwise operations are operations performed on these binary bits. C++ provides several commonly used bitwise operators:

  • Bitwise AND (&): Used to check if a particular bit is set to 1.
  • Bitwise OR (|): Used to set a particular bit to 1.
  • Bitwise XOR (^): Used to flip a particular bit.
  • Bitwise NOT (~): Inverts all the bits.
  • Left Shift («): Shifts all bits to the left by a specified number of positions.
  • Right Shift (»): Shifts all bits to the right by a specified number of positions.

In this example, we need to perform a series of bitwise operations on an unsigned short variable wInfo to represent different states using various flags.

flowchart LR
    A[Original Value: 00010000] --> B[Left Shift: 00010000 << 1]
    B --> C[Result: 00100000]
    C --> D[Right Shift: 00100000 >> 1]
    D --> E[Result: 00010000]

    subgraph Left Shift Operation
        direction LR
        A --> B --> C
    end

    subgraph Right Shift Operation
        direction LR
        C --> D --> E
    end

Requirement Analysis

Based on the description, we have a 16-bit flag to represent different states. These states are represented by various binary bits, with each binary bit corresponding to a specific meaning. For example:

  • bit0: Failure status
  • bit1: Compression status
  • bit2: Incremental status
  • bit3: Presence of subsequent packets
  • bit5: Normal request or cancellation

Using Bitwise Operations

We will use bitwise operations to set and retrieve these flags. Specifically:

  • Bitwise AND: Retrieve the value of a particular bit (0 or 1).
  • Bitwise OR: Set a particular bit to 1.
  • Bitwise XOR: Set a particular bit to 0.

We first define an unsigned short type variable wInfo to store these flags. Then, we use bitwise operations to check and set the corresponding flags.

C++ Example Code

#include <iostream>
#include <bitset>

// Define flag constants
const unsigned short BIT_0_FAIL = 1 << 0;    // bit0 failed?
const unsigned short BIT_1_COMPRESSED = 1 << 1; // bit1 compressed?
const unsigned short BIT_2_INCREMENT = 1 << 2;  // bit2 incremented?
const unsigned short BIT_3_HAS_MORE = 1 << 3;   // bit3 has more packets?
const unsigned short BIT_5_CANCEL = 1 << 5;     // bit5 normal request(0) or cancel(1)

// Check if a bit is set
bool isBitSet(unsigned short wInfo, unsigned short bitMask) {
    return (wInfo & bitMask) != 0;
}

// Set a bit to 1
void setBit(unsigned short& wInfo, unsigned short bitMask) {
    wInfo |= bitMask;
}

// Clear a bit (set it to 0)
void clearBit(unsigned short& wInfo, unsigned short bitMask) {
    wInfo &= ~bitMask;
}

int main() {
    // Assume wInfo's initial value is 0
    unsigned short wInfo = 0;

    // Set bit0 (failure flag)
    setBit(wInfo, BIT_0_FAIL);
    
    // Set bit1 (compressed flag)
    setBit(wInfo, BIT_1_COMPRESSED);
    
    // Print wInfo's binary value
    std::cout << "wInfo (in binary): " << std::bitset<16>(wInfo) << std::endl;

    // Check each flag
    std::cout << "bit0 (failed?): " << (isBitSet(wInfo, BIT_0_FAIL) ? "yes" : "no") << std::endl;
    std::cout << "bit1 (compressed?): " << (isBitSet(wInfo, BIT_1_COMPRESSED) ? "yes" : "no") << std::endl;
    std::cout << "bit2 (incremented?): " << (isBitSet(wInfo, BIT_2_INCREMENT) ? "yes" : "no") << std::endl;
    std::cout << "bit3 (has more packets?): " << (isBitSet(wInfo, BIT_3_HAS_MORE) ? "yes" : "no") << std::endl;
    std::cout << "bit5 (canceled?): " << (isBitSet(wInfo, BIT_5_CANCEL) ? "yes" : "no") << std::endl;

    // Clear bit1 (compressed flag)
    clearBit(wInfo, BIT_1_COMPRESSED);
    
    // Print the updated wInfo
    std::cout << "Updated wInfo (in binary): " << std::bitset<16>(wInfo) << std::endl;

    return 0;
}

Run the code, recommended for old friends: https://wandbox.org/

wInfo (in binary): 0000000000000001
bit0 (failed?): yes
bit1 (compressed?): no
bit2 (incremented?): no
bit3 (has more packets?): no
bit5 (canceled?): no
Updated wInfo (in binary): 0000000000000000

Code Explanation

  1. Flag Definition: Use shift operations (1 << n) to define each flag bit. For example, 1 << 0 corresponds to bit0, 1 << 1 corresponds to bit1, and so on. This way, we allocate a unique binary position for each flag.
  2. Check a Bit: The isBitSet function uses the bitwise AND operation (wInfo & bitMask) to check if a specific flag is set to 1. If the bit is 1, the function returns true, otherwise it returns false.
  3. Set a Bit: The setBit function uses the bitwise OR operation (wInfo |= bitMask) to set a specific flag bit to 1.
  4. Clear a Bit: The clearBit function uses the bitwise AND operation (wInfo &= ~bitMask) to clear a specific flag bit to 0.

Summary

Through bitwise operations, we can efficiently handle multiple state flags. This technique is particularly useful in practical development. For example, in embedded development, network protocols, and system status management scenarios, bit flags are often used to represent multiple binary states, saving space and improving efficiency.

We hope this blog post helps you understand how to use bitwise operations in C++ to perform bitwise selection and setting, and mastering these skills is very helpful for writing efficient and maintainable code!

A financial IT programmer's tinkering and daily life musings
Built with Hugo
Theme Stack designed by Jimmy