|
|
|
|
Shift operators
The shift operators also manipulate bits.
The left-shift operator
(<<) produces the
operand to the left of the operator shifted to the left by the number of bits
specified after the operator. The right-shift operator
(>>) produces the
operand to the left of the operator shifted to the right by the number of bits
specified after the operator. If the value after the shift operator is greater
than the number of bits in the left-hand operand, the result is undefined. If
the left-hand operand is unsigned, the right shift is a logical shift so the
upper bits will be filled with zeros. If the left-hand operand is signed, the
right shift may or may not be a logical shift (that is, the behavior is
undefined).
Shifts can be combined with the equal
sign (<<= and
>>=). The lvalue is
replaced by the lvalue shifted by the rvalue.
What follows is an example that
demonstrates the use of all the operators involving bits. First, here’s a
general-purpose function that prints a byte in binary format, created separately
so that it may be easily reused. The header file declares the
function:
//: C03:printBinary.h
// Display a byte in binary
void printBinary(const unsigned char val);
///:~
Here’s the implementation of the
function:
//: C03:printBinary.cpp {O}
#include <iostream>
void printBinary(const unsigned char val) {
for(int i = 7; i >= 0; i--)
if(val & (1 << i))
std::cout << "1";
else
std::cout << "0";
} ///:~
The printBinary( ) function
takes a single byte and displays it bit-by-bit. The expression
(1 << i)
produces a one in each successive bit
position; in binary: 00000001, 00000010, etc. If this bit is bitwise
anded with val and the result is nonzero, it means there was a one
in that position in val.
Finally, the function is used in the
example that shows the bit-manipulation operators:
//: C03:Bitwise.cpp
//{L} printBinary
// Demonstration of bit manipulation
#include "printBinary.h"
#include <iostream>
using namespace std;
// A macro to save typing:
#define PR(STR, EXPR) \
cout << STR; printBinary(EXPR); cout << endl;
int main() {
unsigned int getval;
unsigned char a, b;
cout << "Enter a number between 0 and 255: ";
cin >> getval; a = getval;
PR("a in binary: ", a);
cout << "Enter a number between 0 and 255: ";
cin >> getval; b = getval;
PR("b in binary: ", b);
PR("a | b = ", a | b);
PR("a & b = ", a & b);
PR("a ^ b = ", a ^ b);
PR("~a = ", ~a);
PR("~b = ", ~b);
// An interesting bit pattern:
unsigned char c = 0x5A;
PR("c in binary: ", c);
a |= c;
PR("a |= c; a = ", a);
b &= c;
PR("b &= c; b = ", b);
b ^= a;
PR("b ^= a; b = ", b);
} ///:~
Once again, a preprocessor macro is used
to save typing. It prints the string of your choice, then the binary
representation of an expression, then a newline.
In main( ), the variables are
unsigned. This is because, in general, you don't want signs when you are
working with bytes. An int must be used instead of a char for
getval because the “cin >>” statement will
otherwise treat the first digit as a character. By assigning getval to
a and b, the value is converted to a single byte (by truncating
it).
The << and >>
provide bit-shifting behavior, but when they shift bits
off the end of the number, those bits are lost (it’s commonly said that
they fall into the mythical bit
bucket, a place where discarded bits end up, presumably so they can be
reused...). When manipulating bits you can also perform
rotation, which means that the bits that fall off
one end are inserted back at the other end, as if they’re being rotated
around a loop. Even though most computer processors provide a machine-level
rotate command (so you’ll see it in the assembly language for that
processor), there is no direct support for “rotate” in C or C++.
Presumably the designers of C felt justified in leaving
“rotate” off (aiming, as they said, for a minimal language) because
you can build your own rotate command. For example, here are functions to
perform left and right rotations:
//: C03:Rotation.cpp {O}
// Perform left and right rotations
unsigned char rol(unsigned char val) {
int highbit;
if(val & 0x80) // 0x80 is the high bit only
highbit = 1;
else
highbit = 0;
// Left shift (bottom bit becomes 0):
val <<= 1;
// Rotate the high bit onto the bottom:
val |= highbit;
return val;
}
unsigned char ror(unsigned char val) {
int lowbit;
if(val & 1) // Check the low bit
lowbit = 1;
else
lowbit = 0;
val >>= 1; // Right shift by one position
// Rotate the low bit onto the top:
val |= (lowbit << 7);
return val;
} ///:~
Try using these functions in
Bitwise.cpp. Notice the definitions (or at least declarations) of
rol( ) and ror( ) must be seen by the compiler in
Bitwise.cpp before the functions are used.
The bitwise functions are generally
extremely efficient to use because they translate directly into assembly
language statements. Sometimes a single C or C++ statement will generate a
single line of assembly
code.
|
|
|