Bitwise operators perform simple logic:
OR: If either or both bits is 1, result is 1. Otherwise result is zero:
1 2 3 4
|
0 | 0 == 0
0 | 1 == 1
1 | 0 == 1
1 | 1 == 1
|
AND: If both bits are 1, result is 1. Otherwise result is zero:
1 2 3 4
|
0 & 0 == 0
0 & 1 == 0
1 & 0 == 0
1 & 1 == 1
|
XOR: If either (but not both) bits are 1, result is 1. Otherwise (both bits zero, or both bits 1), result is zero:
1 2 3 4
|
0 ^ 0 == 0
0 ^ 1 == 1
1 ^ 0 == 1
1 ^ 1 == 0
|
COMPLIMENT: Effectively a bitwise "not". If the bit is 0, result is 1, otherwise result is 0:
The C++ operators perform these actions on each bit in the given values. For example:
1 2 3 4 5 6 7 8 9 10
|
int foo = 0x13 & 0x05;
cout << foo; // prints 1
// because...
/*
0x13 = %0001 0011
0x05 = %0000 0101
---- & ----------
0x01 = %0000 0001
*/
|
Practical Uses: OR
OR is useful for making sure an individual bit is set. This is useful when combining "flags" as Cubbi illustrated:
1 2 3 4 5 6 7 8 9
|
// a flag would be a unique bit
const int flag1 = 0x01; // %0001
const int flag2 = 0x02; // %0010
const int flag3 = 0x04; // %0100
const int flag4 = 0x08; // %1000
// to combine flags:
int someflags = flag1 | flag3;
// result is: 0x05 : %0101
|
The way this works is by "turning on" the desired bit, and leaving all other bits unchanged.
if we do: someflags |= flag1;
someflags = %????
flag1 = %0001
| -----
%???1 <- all bits stay unchanged except the low bit, which becomes 1
|
This is different from normal addition because addition will screw up the values if the flag is already set:
1 2 3
|
int someflags = flag1 | flag3; // 0x05 : %0101
int good = someflags | flag3; // still 0x05 : %0101
int bad = someflags + flag3; // now 0x09 : %1001
|
As you can see, by using addition, we've actually cleared the desired flag and set an undesired one by mistake!
Practical Uses: AND, compliment
AND is often used to see if a specific flag has been set:
1 2 3 4
|
if( someflags & flag1 )
{
// here, we know flag1 is set in someflags
}
|
The way this works is by "turning off" all bits except the bits we're interested in (in this case, flag1's bit):
someflags = %????
flag1 = %0001
& -----
%000? <- all bits become zero except for the low bit
|
AND can also be used with the compliment operator to "turn off" a specific bit. Kind of like the reverse of the OR operation:
if we do: someflags &= ~flag1;
flag1 = %0001
~flag1 = %1110
someflags = %????
~flag1 = %1110
& -----
%???0 <- all bits unchanged except the low bit, which is forced to zero
|
Practical Uses: XOR
I'm too lazy to get into how this is commonly used. XOR effectively lets you toggle a bit:
someflags ^= flag1;
will set flag1 if it's clear, or clear it if it's already set.
Mostly this is used for encrypting stuff, since XOR doesn't "lose" any information. If you XOR a value with the same key twice, you get the original value.
That is:
1 2 3 4 5 6 7
|
int original = ...;
int key = ...;
int encypted = original ^ key;
int decrypted = encrypted ^ key;
if(original == decrypted) // <- this will be true, regardless of what 'original' or 'key' are
|