Applications of bitwise operators in C and their efficiency?

Sai Maddali picture Sai Maddali · Jun 19, 2013 · Viewed 12.1k times · Source

I am new to bitwise operators.

I understand how the logic functions work to get the final result. For example, when you bitwise AND two numbers, the final result is going to be the AND of those two numbers (1 & 0 = 0; 1 & 1 = 1; 0 & 0 = 0). Same with OR, XOR, and NOT.

What I don't understand is their application. I tried looking everywhere and most of them just explain how bitwise operations work. Of all the bitwise operators I only understand the application of shift operators (multiplication and division). I also came across masking. I understand that masking is done using bitwise AND but what exactly is its purpose and where and how can I use it?

Can you elaborate on how I can use masking? Are there similar uses for OR and XOR?

Answer

jxh picture jxh · Jun 19, 2013

The low-level use case for the bitwise operators is to perform base 2 math. There is the well known trick to test if a number is a power of 2:

if ((x & (x - 1)) == 0) {
    printf("%d is a power of 2\n", x);
}

But, it can also serve a higher level function: set manipulation. You can think of a collection of bits as a set. To explain, let each bit in a byte to represent 8 distinct items, say the planets in our solar system (Pluto is no longer considered a planet, so 8 bits are enough!):

#define Mercury (1 << 0)
#define Venus   (1 << 1)
#define Earth   (1 << 2)
#define Mars    (1 << 3)
#define Jupiter (1 << 4)
#define Saturn  (1 << 5)
#define Uranus  (1 << 6)
#define Neptune (1 << 7)

Then, we can form a collection of planets (a subset) like using |:

unsigned char Giants = (Jupiter|Saturn|Uranus|Neptune);
unsigned char Visited = (Venus|Earth|Mars);
unsigned char BeyondTheBelt = (Jupiter|Saturn|Uranus|Neptune);
unsigned char All = (Mercury|Venus|Earth|Mars|Jupiter|Saturn|Uranus|Neptune);

Now, you can use a & to test if two sets have an intersection:

if (Visited & Giants) {
    puts("we might be giants");
}

The ^ operation is often used to see what is different between two sets (the union of the sets minus their intersection):

if (Giants ^ BeyondTheBelt) {
    puts("there are non-giants out there");
}

So, think of | as union, & as intersection, and ^ as union minus the intersection.

Once you buy into the idea of bits representing a set, then the bitwise operations are naturally there to help manipulate those sets.