You might want to represent 32 boolean variables very compactly. You can use an int variable (assuming it has 4 bytes) as a 32 bit Boolean array. Unlike the bool type in C++, which presumably requires one byte, you can make your own Boolean variable using a single bit. However, to do so, you need to access individual bits.
Another reason. Sometimes you are reading input from a device. Each bit may indicate a status for the device or it may be one bit of control for that device.
Bit manipulation is considered really low-level programming. There are some who believe that being able to access the individual bits is bad abstraction. Thus, many higher level languages hide the representation from the programmer. However, languages like C are often used for systems programming where data representation is quite important.
char ch ;How might you set the least significant bit to 1? You might be tempted to do the following:
ch[ 0 ] = 1 ; // WRONG! Does not set bit 0 of chAfter all, aren't arrays wonderful? Why shouldn't this work? Why shouldn't C allow us this nice and simple way to manipulate bits? Alas, it doesn't.
The reason has to do with how arrays are defined in C.
arr[ i ] is really defined as *( arr + i ) where
arr is & arr[ 0 ] and + i does pointer
arithmetic. The compiler performs the following computatation.
address of arr[ i ] is
addr( arr ) + ( i * sizeof(type of array element) )
Thus, you find out what address arr is at, then multiply
i by the number of bytes for the type of one element in the
array. This creates an address. Recall that addresses in memory
refer to bytes, not to individual bits. That's where the problem
lies. The semantics of array access refer to memory addresses of
bytes.
So, we need to find other ways to access individual bits. As you might guess, one way to do this is to use bitshift and bitwise operators.
We're going to consider bitstring pattern called a mask. Here's one example of a mask.
| b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
| 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
This is a bitstring with exactly one bit that has a value of 1. In this example, b3 = 0.
Similarly, the bitflipped version of this is also considered a mask.
| b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
| 1 | 1 | 1 | 1 | 0 | 1 | 1 | 1 |
What's the purpose of a mask? The purpose is to either access an individual bit (or range of bits) or to modify an individual bit (or range of bits).
unsigned char mask = static_castWhat does 1 << i do? The problem is what type is 1? It's most likely an int, which means that it's mostly like 31 zeroes followed by a 1. So, 1 << i causes bi = 1 while the rest of the bits are 0.( 1 << i ) ;
This creates the first kind of mask shown above.
To create the second kind, you can do bitwise negation on the
mask from the first part, as in:
unsigned char mask = static_cast
You might wonder if it's strictly necessary to do the static
cast (at least, in C++)? The answer depends. If the compiler
doesn't complain, then you don't need to do it.
Here's the prototype:
bool isBitISet( unsigned char ch, int i ) ;
Here's the code.
bool isBitISet( unsigned char ch, int i )
{
unsigned char mask = static_cast
It looks pretty simple, doesn't it? Create a mask, then use
bitwise AND on the character.
Here's why it works.
| ch | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 |
| mask | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
| result = ch & mask | 0 | 0 | 0 | 0 | b3 | 0 | 0 | 0 |
We don't particularly care what the character looks like. Just assume it's some 8-bit bitstring, b7...b0. To distinguish the character from the mask, let's label the bits of the mask as m7...m0.
The mask has bit m3 set, though 3 was picked arbitrarily. Notice what happens when you bitwise AND the mask and the character.
The bits which are 0 in the mask cause the result to also be 0 (since 0 & bi = 0). Bit b3 is ANDed with the '1' appearing in the mask at position m3. That results in b3.
If b3 = 1, then result is non-zero. If b3 = 0, then result is zero.
The only interesting part is the return type. The result of the AND operation is an unsigned char. We're returning a bool (in C, you'd return an int or a short).
In C++, converting between char, int, and bool rarely requires a cast. Basically, anything that evaluates to 0 for a given type, is false when converted to a bool Anything that evaluates to a non-zero value (which doesn't have to be 1), evaluates to true.