Sorry about the delay.
So when you declare a variable of integer type (char, short, int, long and their unsigned versions) then a lot of the time you use that variable to store a number using normal assignment and arithmetic operators.
However the variable is represented in memory by a fixed number of bits for any variable V this is equal to
sizeof v * CHAR_BIT
CHAR_BIT is defined in limits.h and is typically 8 but can have other values as it is platform dependent.
Typically the number of bits in various integer types are
char 8 bits
short 16 bits
int 16 or 32 bits
long 32 bits
More and more platforms also support a 64 bit integer type.
Bit masking occurs when you decided that rather than treat an integer variable as a value you will use it's individual bits to represent different values. You may want to do this to save memory or to mimic some hardware or for other reasons.
In order to access the individual bits of the variable then we have to use the bitwise operators as follows
& AND
| OR
^ XOR
~ NOT
to allow us to access the individual bits. When you use a bitwise operator the variables are acted on a bit at a time, that is in the expression
c = a & b;
bit 0 of c(the result) is the result of ANDing bit 0 and a and bit 0 of bit, the result does not effect any other bits in c. Put another way the bits are all handled in isolation from each other.
AND (&) is a binary operator and outputs 1 if both inputs are 1 otherwise it outputs 0.
OR (|) is a binary operator and outputs 1 if either input is 1 otherwise it outputs 0
XOR (^) is a binary operator and outputs 1 if either but not both inputs are 1 otherwise it outputs 0
NOT (~) is a unary operator, it inverts the bit value, i.e. outputs 1 if it was 0 and 0 if it was 1.
AND, OR, XOR and NOT are all
associative.
Other useful operators when access bits are
>> shift left
<< shift right
Note that the implementation of the shift operators (particularly left shift) is platform dependent and can either be an arithmetic shift or a logical shift. For this reason among others it is best practice when using an integer variable by accessing it's bits directly to do so on an unsigned version of the type that you wish to use.
Onto an example, take this structure definition
-
#define HD_FLAG_TEA 0x01 // Note if the preference isn't tea then it's coffee
-
#define HD_FLAG_MILK 0x02
-
#define HD_FLAG_SUGER 0x04
-
#define HD_FLAG_LEMON 0x08
-
-
typedef struct hot_drink_preference {
-
char temperature; // Prefered Drink Temerature in Celsius
-
unsigned char flags; // Mask of bits containing other drink preferences
-
} HDP;
-
-
HDP BanfaDP;
-
-
BanfaDP.temperature = 86;
-
BanfaDP.flags = HD_FLAG_MILK;
-
So the structure has 2 members, temperature representing the prefered temperature of the drink and flags which is a bit mask of other preferences.
Also note I have some #defines, these are the bits that I will be using in the flags member, look at the values they are defined to, note that they are equivilent to the binary values
0001
0010
0100
1000
There are no overlapping bits, and of course the is not 0 flag, these #defines do not represent the value that indicates if a particular preference is liked, the represent the bit position of the bit that indicates if a particular preference is liked.
So what operations would I like to do on my flags member, well common ones are
test a particular bit
set a particular bit
clear a particular bit
test a particular bit, return true or false. Not in bitwise operations (and in C in general) 0 is false any other value is true, if fact it might to better to expresses the 2 states as false and NOT false.
When I test a bit I am uninterested in the all the other bits, I want the result to have a 0 for all bits I am not interested in and the value of the bit I am interested in. Then if the bit of interest is 0 the result will be 0 (false) and if the bit of interest is 1 then result will be the value of that bit (NOT false). The AND operator does this
result = flags & bitOfInterest;
result will either have the value 0 or bitOfInterest after this statement
using the above example
-
if (BanfaDP.flags & HD_FLAG_SUGER)
-
{
-
printf("Banfa takes suger\n");
-
}
-
else
-
{
-
printf("Banfa doesn't take suger\n");
-
}
-
set a particular bit, setting a bit means giving it the value 1. So for any bit not of interest I want the result to have value of that original bit. For the bit of interest the result should have a value of 1 regardless of the original value. This is the OR operation because
x OR 0 = x
x OR 1 = 1
so
result = flags | bitOfInterest;
again as an example from above
-
// Set Suger as a drink perference for Banfa
-
BanfaDP.flags |= HD_FLAG_SUGER;
-
clear a particular bit, clearing a bit means giving it the value 0. Bits not of interest should have the same value in the result as they started, the bit of interest should end with a value of 0 regardless of what it's starting value is. So where OP stands for some undefined operation
x OP 0 = x <- Bits not of interest
x OP 1 = 0 <- Bit of interest
The answer may not be obvious because it requires 2 operations, first we have to invert the bitOfInterest value using NOT
x OP NOT(0) = x <- Bits not of interest
x OP NOT(1) = 0 <- Bit of interest
Giving
x OP 1 = x <- Bits not of interest
x OP 0 = 0 <- Bit of interest
You may recogniste this as AND so we have
result = flags & ~bitOfInterest;
from the example
-
// Clear Suger as a drink perference for Banfa
-
BanfaDP.flags &= ~HD_FLAG_SUGER;
-
Because of the associativeity of the bitwise operators you can set and clear more than 1 option at a time.
-
// Set Tea and Lemon as a drink perference for Banfa
-
BanfaDP.flags |= HD_FLAG_TEA | HD_FLAG_LEMON;
-
-
// Clear Tea and Lemon as a drink perference for Banfa
-
BanfaDP.flags &= ~(HD_FLAG_TEA | HD_FLAG_LEMON);
-
-
// Set drink preferences clearing all other options
-
BanfaDP.flags = HD_FLAG_MILK;
-
You can test multiple bits at the same time but it is not quite so straight forward
-
// OR
-
if (BanfaDP.flags & (HD_FLAG_MILK | HD_FLAG_SUGER))
-
{
-
// Banfa takes milk or suger
-
}
-
-
// AND
-
if (BanfaDP.flags & (HD_FLAG_MILK | HD_FLAG_SUGER) == (HD_FLAG_MILK | HD_FLAG_SUGER))
-
{
-
// Banfa takes milk and suger
-
}
-
I suggest you try some of this out yourself in a test program.