FEBRUARY 17, 2025
UNDERSTANDING
BIT MANIPULATIONS IN C PROGRAMMING LANGUAGE
                                              VAMSI KRISHNA VAKA
What is Bit Manipulation?
Bit manipulation is the process of directly operating on individual bits of data using bitwise operators. It is
widely used in embedded systems, cryptography, graphics, networking, and data compression due to its
efficiency in performing low-level operations.
Bitwise Operators in C
Bitwise operators allow direct manipulation of bits in variables. They work only on integer data types (int,
char, short, long, etc.).
1. Bitwise AND (&)
Syntax:
            result = operand1 & operand2;
Functionality:
     •   Compares each corresponding bit in two numbers.
     •   Returns 1 if both bits are 1, otherwise returns 0.
     •   Used for bit masking (extracting specific bits).
Example:
#include <stdio.h>
int main()
    int a = 5, b = 3;                          // a = 0101, b = 0011
    int result = a & b;                        // 0101 & 0011 = 0001 (1 in decimal)
    printf("Bitwise AND: %d\n", result);
    return 0;
Output:
Bitwise AND: 1
2. Bitwise OR (|)
Syntax:
                result = operand1 | operand2;
Functionality:
     •   Compares each bit in two numbers.
     •   Returns 1 if at least one of the bits is 1.
     •   Used to set specific bits.
Example:
#include <stdio.h>
int main()
{
    int a = 5, b = 3;            // a = 0101, b = 0011
    int result = a | b;         // 0101 | 0011 = 0111 (7 in decimal)
    printf("Bitwise OR: %d\n", result);
    return 0;
Output:
Bitwise OR: 7
3. Bitwise XOR (^)
Syntax:
            result = operand1 ^ operand2;
Functionality:
     •   Returns 1 if bits are different, otherwise returns 0.
     •   Used to toggle bits (flip 1 to 0 and vice versa).
Example:
#include <stdio.h>
int main()
    int a = 5, b = 3;                    // a = 0101, b = 0011
    int result = a ^ b;                  // 0101 ^ 0011 = 0110 (6 in decimal)
    printf("Bitwise XOR: %d\n", result);
    return 0;
Output:
Bitwise XOR: 6
4. Bitwise NOT (~)
Syntax:
                 result = ~operand;
Functionality:
     •   Inverts all bits (1 becomes 0, 0 becomes 1).
     •   Used for bit inversion.
     •   Note: The result is usually negative due to two’s complement representation.
Example:
#include <stdio.h>
int main()
{
    int a = 5;              // a = 00000101
    int result = ~a;        // ~00000101 = 11111010 (-6 in decimal)
    printf("Bitwise NOT: %d\n", result);
    return 0;
Output:
Bitwise NOT: -6
5. Left Shift (<<)
Syntax:
         result = operand << n;
Functionality:
     •   Shifts bits left by n positions.
     •   Fills vacant bits with 0.
     •   Equivalent to multiplying by 2^n.
Example:
#include <stdio.h>
int main()
    int a = 5; // a = 00000101
    int result = a << 2; // 00010100 (20 in decimal)
    printf("Left Shift: %d\n", result);
    return 0;
}
Output:
Left Shift: 20
6. Right Shift (>>)
Syntax:
         result = operand >> n;
Functionality:
     •   Shifts bits right by n positions.
     •   Fills leftmost bits with 0 (for unsigned numbers) or sign bit (for signed numbers).
     •   Equivalent to dividing by 2^n.
Example:
#include <stdio.h>
int main() {
    int a = 20;                 // a = 00010100
    int result = a >> 2;         // 00000101 (5 in decimal)
    printf("Right Shift: %d\n", result);
    return 0;
Output:
Right Shift: 5
Problems to Solve
Basic Level
     1. Write a C program to check whether a given number is odd or even using bitwise operators.
     2. Implement a program to swap two numbers without using a third variable, using bitwise XOR.
     3. Write a program to find the nth bit of a number.
Intermediate Level
     4. Implement a function to count the number of set bits (1s) in a number.
        5. Write a program to toggle a particular bit of a number.
        6. Implement a function to check if a number is a power of 2.
Advanced Level
        7. Given an integer array, find the unique element (all other elements appear twice).
        8. Implement a function to reverse the bits of an integer.
        9. Write a function to find the next higher power of 2 for a given number.
Problems:
        1. Find the number of set bits in a given number.
#include <stdio.h>
int countSetBits(int num)
{
    int count = 0;
    while (num)
         count += num & 1;                                   // Check the last bit
         num >>= 1;                                         // Right shift to process next bit
    return count;
}
int main()
    int num = 29;                                       // 29 = 11101 (4 set bits)
    printf("Number of set bits: %d\n", countSetBits(num));
    return 0;
}
Output:
Number of set bits: 4
2. Checking if a Bit is Set
This checks whether a specific bit in a number is 1.
#include <stdio.h>
                                      // Function to check if a bit is set
int isBitSet(int num, int pos)
{
  return (num & (1 << pos)) ? 1 : 0;
}
int main()
{
  int num = 0b10110110;                // 182 in decimal
  int pos = 5;                       // Checking bit at position 5 (0-based index)
    if (isBitSet(num, pos))
       printf("Bit %d is SET in number %d (Binary: %08b)\n", pos, num, num);
    else
       printf("Bit %d is NOT set in number %d (Binary: %08b)\n", pos, num, num);
    return 0;
}
Explanation:
     •   1 << pos shifts 1 to the specified bit position.
     •   num & (1 << pos) checks if that bit is set (1) or not (0).
     •   If the result is nonzero, the bit is set.
Example Output:
Bit 5 is SET
2. Setting a Bit
This sets a particular bit to 1 without affecting other bits.
#include <stdio.h>
int setBit(int num, int pos)
    return num | (1 << pos);
}
int main()
    int num = 0b10110110;               // 182
    int pos = 2;
    int newNum = setBit(num, pos);
    printf("New number after setting bit %d: %d (Binary: %08b)\n", pos, newNum, newNum);
    return 0;
}
Explanation:
     •   1 << pos creates a mask with 1 at the given position.
     •   num | (1 << pos) ensures that bit remains 1 even if it was already 1.
Example Output:
New number after setting bit 2: 182 (Binary: 10111110)
3. Clearing a Bit
This clears a particular bit (sets it to 0) without affecting other bits.
#include <stdio.h>
int clearBit(int num, int pos)
    return num & ~(1 << pos);
}
int main()
    int num = 0b10110110; // 182
    int pos = 5;
    int newNum = clearBit(num, pos);
    printf("New number after clearing bit %d: %d (Binary: %08b)\n", pos, newNum, newNum);
    return 0;
}
Explanation:
     •   1 << pos creates a mask with 1 at the target position.
     •   ~(1 << pos) flips all bits except the target bit.
     •   num & ~(1 << pos) forces the bit to be 0.
Example Output:
New number after clearing bit 5: 150 (Binary: 10010110)
4. Toggling a Bit
This flips a bit (from 0 to 1 or 1 to 0).
#include <stdio.h>
int toggleBit(int num, int pos)
    return num ^ (1 << pos);
}
int main()
    int num = 0b10110110; // 182
    int pos = 3;
    int newNum = toggleBit(num, pos);
    printf("New number after toggling bit %d: %d (Binary: %08b)\n", pos, newNum, newNum);
    return 0;
}
Explanation:
        •     1 << pos creates a mask at the target bit.
        •     num ^ (1 << pos) flips that bit.
Example Output:
New number after toggling bit 3: 174 (Binary: 10101110)
5. Counting the Number of Set Bits (Hamming Weight)
This counts how many 1s are present in a number.
#include <stdio.h>
int countSetBits(int num)
    int count = 0;
    while (num)
    {
            count += num & 1; // Check LSB
            num >>= 1;        // Right shift
    }
    return count;
}
int main()
    int num = 0b10110110;                       // 182
    printf("Number of set bits: %d\n", countSetBits(num));
    return 0;
Explanation:
     •     num & 1 checks if the least significant bit (LSB) is 1.
     •     num >>= 1 shifts bits right, checking one bit at a time.
Example Output:
Number of set bits: 5
6. Checking if a Number is Power of 2
This determines if a number is a power of 2 (i.e., only one bit is set).
#include <stdio.h>
int isPowerOfTwo(int num)
{
    return (num > 0) && ((num & (num - 1)) == 0);
}
int main()
{
    int num = 16;
    if (isPowerOfTwo(num))
         printf("%d is a power of 2\n", num);
    else
         printf("%d is NOT a power of 2\n", num);
    return 0;
Explanation:
     •     A power of 2 has only one 1 bit (e.g., 0001, 0010, 0100).
     •     num & (num - 1) removes the lowest 1 bit.
           If the result is 0, it's a power of 2.
Example Output:
16 is a power of 2
7. Reversing Bits of an Integer
This reverses the binary representation of a number.
#include <stdio.h>
unsigned int reverseBits(unsigned int num)
    unsigned int rev = 0;
    for (int i = 0; i < 8; i++)
    {                                                       // Assume 8-bit number
            rev |= ((num >> i) & 1) << (7 - i);
    }
    return rev;
int main()
{
    unsigned int num = 0b10110110; // 182
    printf("Reversed bits: %d (Binary: %08b)\n", reverseBits(num), reverseBits(num));
    return 0;
}
Explanation:
        •     Extract each bit and place it in the reverse position.
Example Output:
Reversed bits: 109 (Binary: 01101101)
Bit manipulation is heavily used in embedded systems for efficient low-level programming. Here are some
real-world examples where bitwise operations are used:
1. Setting, Clearing, and Toggling GPIO Pins
Use Case:
Embedded systems often control GPIO (General Purpose Input/Output) pins for interfacing with sensors,
LEDs, motors, etc.
Example:
#define GPIO_PORT (*(volatile unsigned int*)0x40021000) // Example GPIO Register
#define LED_PIN        (1 << 5) // LED connected to bit 5
void turnOnLED()
{
    GPIO_PORT |= LED_PIN;                  // Set bit 5 (Turn ON LED)
void turnOffLED()
{
    GPIO_PORT &= ~LED_PIN;                 // Clear bit 5 (Turn OFF LED)
void toggleLED()
{
    GPIO_PORT ^= LED_PIN;                    // Toggle bit 5 (Flip LED state)
Explanation:
     •   |= (OR) sets the bit to 1 (turns LED ON).
     •   &= ~ (AND with NOT) clears the bit to 0 (turns LED OFF).
     •   ^= (XOR) toggles the bit (switches LED state).
2. Checking if a Bit is Set (Reading Sensor Status)
Use Case:
Reading button press or sensor status from an input pin.
Example:
#define GPIO_PORT (*(volatile unsigned int*)0x40021000) // Example GPIO Register
#define SENSOR_PIN (1 << 3)          // Sensor connected to bit 3
int isSensorActive()
{
    return (GPIO_PORT & SENSOR_PIN) ? 1 : 0; // Check if bit 3 is HIGH
}
Explanation:
     •   & (AND) is used to check if a specific bit (SENSOR_PIN) is set (1).
     •   If set, it returns 1 (sensor is active), else 0.
3.Efficient Data Packing in Embedded Systems
Use Case:
Reducing memory usage by storing multiple values in a single variable.
Example:
Storing sensor data (temperature, pressure, humidity) in a single 32-bit integer.
#include <stdio.h>
void packData(int *data, int temp, int pressure, int humidity)
{
    *data = (temp & 0xFF) | ((pressure & 0xFF) << 8) | ((humidity & 0xFF) << 16);
void unpackData(int data, int *temp, int *pressure, int *humidity)
    *temp = data & 0xFF;
    *pressure = (data >> 8) & 0xFF;
    *humidity = (data >> 16) & 0xFF;
}
int main()
    int data;
    packData(&data, 25, 101, 60); // Store values
    int t, p, h;
    unpackData(data, &t, &p, &h); // Extract values
    printf("Temp: %d, Pressure: %d, Humidity: %d\n", t, p, h);
    return 0;
}
Explanation:
     •   Packing: Uses | (OR) and bit shifts to combine values.
     •   Unpacking: Uses & (AND) and bit shifts to extract values.
4. Using Bit Fields in Structs to Optimize Memory
Use Case:
In embedded systems, memory is limited. Bit fields allow packing multiple values into a single byte.
Example:
#include <stdio.h>
struct DeviceStatus
     unsigned int power : 1; // 1 bit
     unsigned int error : 1; // 1 bit
     unsigned int mode : 2; // 2 bits (00, 01, 10, 11)
};
int main()
     struct DeviceStatus status = {1, 0, 2}; // Power ON, No Error, Mode 2
     printf("Power: %d, Error: %d, Mode: %d\n", status.power, status.error, status.mode);
     return 0;
}
Explanation:
      •   Saves memory by using only the required bits instead of whole bytes.
Conclusion
Mastering bit manipulation is crucial in embedded systems and low-level programming. It optimizes
performance, reduces memory usage, and enhances hardware interaction. By practicing the above problems,
you’ll gain proficiency in handling bit-level operations efficiently.