A little computer knowledge before we go into bitwise operators and example code:
A byte has eight bits, so in a four byte long variable, we can hold 32 bits inside of "it". In essence we can hold 32 different flags. A bit is "set" if its value is 1 and clear when its value is 0. We can set and clear bits by changing the value of the long variable, but it can get tedious and often frustrating (especially if we are just playing around).
C++ provides four bitwise operators that act upon individual bits of a variable. They are similar to logical operators, but please don't confuse them--this will lead to quite unexpected behavior by your program. The operators are as follows:
& AND
| OR
^ exclusive OR
~ complement
What follows is a little dive into math theory, it is by no means a complete work on the subject.
Operator AND
The AND operator is a single ampersand. When you AND two bits, the result is 1 if both bits are 1, but 0 if both or either one of the bits is 0. A good way to think about it is both bits must be "set" for this to equal 1.
Operator OR
This second bitwise operator is OR. The result is 1 if either bit is set and only zero if neither bit is set.
Exclusive OR
When you exclusive OR two bits, the result is 1 if the bits are different. The result is 0 if both bits are the same (i.e. both set or both not set).
Complement
The complement operator clears every bit in a number that is set and sets every bit that is clear. A complete swap in layman's terms. Example:
Original: 1010 0011
New: 0101 1100
Now we get into some complicated mess. It is worth noting that programming today is a helluva lot easier then in past times. (I speak not from experience, but from what I have been told and what I read). We have high level language where we do not have to manipulate bits on a daily basis.
Setting Bits:
When we want to set or clear a particular bit, we use a masking operation. If we have the four byte flag mentioned earlier and we want to set bit 8 so that it is true (on or "set"), we need to OR the flag with the value 128. Why 128 you ask? Because 128 is 1000 0000 in binary, therefore the value of the eighth bit is 128. Whatever the current value of the bit you want to manipulate is, if you OR it with 128, it will only change that bit and none of the others. Here is an example of what I'm talking about:
cpp
-----------9 8765 4321
- 1010 0110 0010 0110 //bit 8 is clear (0)
| 0000 0000 1000 0000 // 128 OR
------------------------------
1010 0110 1010 0110 // new value with bit 8 changed
Bits are counted from right to left.
Clearing bits:
If we want to clear that same bit we can AND the bit with the complement of 128:
cpp
--1010 0110 1010 0110 //bit 8 is now set
& 1111 1111 0111 1111 //~128
-----------------------------
1010 0110 0010 0110 //bit 8 is now cleared
Flipping bits:
To flip a bit back to its original value, exclusive OR it twice with the same number.
Bit Fields:
Now we get to a practical application of using bits. Using standard C++ data types the smallest regular variable we could use for argument's sake is a char. It is one byte. This is eight bits. In most circumstances you will probably use an int which is most often a 4 byte data piece on a 32 bit processor. What if you need to use something smaller? Enter bit fields.
They work like this: Bit fields are named and accessed the same way as any class member. Their type is always an unsigned int. After the bit field name, type a colon, followed by a number. this number determines how many values the bit field can have.
1 - the bit can either represent 0 or 1
2 - the bit can represent 0, 1, 2, 3
It goes up exponentially 2^n where n is the number you chose. Example:
cpp
unsigned int myStatus: 1; //stats can either be 0 or 1, i.e. on or off, true or false, very handy!
Here is a small code example how how to implement a bit field in a simplistic manner (bit fields are used in classes):
cpp
/*
* Example on how to use bit
* fields to consume less memory
* but provide equal ability and function
*/
#include <iostream>
using namespace std;
enum STATUS { FullTime, PartTime };
class student
{
public:
student():
myStatus(FullTime)
{}
~student(){}
void setStatus (STATUS);
STATUS getStatus();
private:
unsigned myStatus : 1; //bit field
};
void student::setStatus(STATUS theStatus)
{
myStatus = theStatus; // set the bit field
}
STATUS student::getStatus()
{
if(myStatus)
return FullTime;
else
return PartTime;
}
int main()
{
student Bob;
if(Bob.getStatus()==PartTime)
cout << "Bob is a part time student.\n";
else
cout << "Bob is a full time student.\n";
Bob.setStatus(PartTime); //change the bit field
if(Bob.getStatus()==PartTime)
cout << "Bob is a part time student.\n";
else
cout << "Bob is a full time student.\n";
return 0;
}
The bit field is initialized in the object constructor and then later changed. Enumerating one and zero can make this process much easier to understand especially if you have multiple bit fields.
Hope you all enjoyed this; now go out and play with some ones and zeros!
edited for some typos I found