C++ provides extensive bit-manipulation capabilities for programmers who need to get down to the so-called "bits-and-bytes" level. Operating systems, test-equipment software, networking software and many other kinds of software require that the programmer communicate "directly with the hardware." In this and the next several sections, we discuss C++'s bit-manipulation capabilities. We introduce each of C++'s many bitwise operators, and we discuss how to save memory by using bit fields.
All data is represented internally by computers as sequences of bits. Each bit can assume the value 0 or the value 1. On most systems, a sequence of 8 bits forms a bytethe standard storage unit for a variable of type char. Other data types are stored in larger numbers of bytes. Bitwise operators are used to manipulate the bits of integral operands (char, short, int and long; both signed and unsigned). Unsigned integers are normally used with the bitwise operators.
Portability Tip 22.3
Bitwise data manipulations are machine dependent. |
Note that the bitwise operator discussions in this section show the binary representations of the integer operands. For a detailed explanation of the binary (also called base-2) number system, see Appendix D, Number Systems. Because of the machine-dependent nature of bitwise manipulations, some of these programs might not work on your system without modification.
The bitwise operators are: bitwise AND (&), bitwise inclusive OR (|), itwise exclusive OR (^), left shift (<<), right shift (>>) and bitwise complement (~)also known as the one's complement. (Note that we have been using &, << and >> for other purposes. This is a classic example of operator overloading.) The bitwise AND, bitwise inclusive OR and bitwise exclusive OR operators compare their two operands bit by bit. The bitwise AND operator sets each bit in the result to 1 if the corresponding bit in both operands is 1. The bitwise inclusive-OR operator sets each bit in the result to 1 if the corresponding bit in either (or both) operand(s) is 1. The bitwise exclusive-OR operator sets each bit in the result to 1 if the corresponding bit in either operandbut not bothis 1. The left-shift operator shifts the bits of its left operand to the left by the number of bits specified in its right operand. The right-shift operator shifts the bits in its left operand to the right by the number of bits specified in its right operand. The bitwise complement operator sets all 0 bits in its operand to 1 in the result and sets all 1 bits in its operand to 0 in the result. Detailed discussions of each bitwise operator appear in the following examples. The bitwise operators are summarized in Fig. 22.5.
Operator |
Name |
Description |
---|---|---|
& |
bitwise AND |
The bits in the result are set to 1 if the corresponding bits in the two operands are both 1. |
| |
bitwise inclusive OR |
The bits in the result are set to 1 if one or both of the corresponding bits in the two operands is 1. |
^ |
bitwise exclusive OR |
The bits in the result are set to 1 if exactly one of the corresponding bits in the two operands is 1. |
<< |
left shift |
Shifts the bits of the first operand left by the number of bits specified by the second operand; fill from right with 0 bits. |
>> |
right shift with sign extension |
Shifts the bits of the first operand right by the number of bits specified by the second operand; the method of filling from the left is machine dependent. |
~ |
bitwise complement |
All 0 bits are set to 1 and all 1 bits are set to 0. |
Printing a Binary Representation of an Integral Value
When using the bitwise operators, it is useful to illustrate their precise effects by printing values in their binary representation. The program of Fig. 22.6 prints an unsigned integer in its binary representation in groups of eight bits each.
Figure 22.6. Printing an unsigned integer in bits.
(This item is displayed on pages 1066 - 1067 in the print version)
1 // Fig. 22.6: fig22_06.cpp 2 // Printing an unsigned integer in bits. 3 #include 4 using std::cout; 5 using std::cin; 6 using std::endl; 7 8 #include 9 using std::setw; 10 11 void displayBits( unsigned ); // prototype 12 13 int main() 14 { 15 unsigned inputValue; // integral value to print in binary 16 17 cout << "Enter an unsigned integer: "; 18 cin >> inputValue; 19 displayBits( inputValue ); 20 return 0; 21 } // end main 22 23 // display bits of an unsigned integer value 24 void displayBits( unsigned value ) 25 { 26 const int SHIFT = 8 * sizeof( unsigned ) - 1; 27 const unsigned MASK = 1 << SHIFT; 28 29 cout << setw( 10 ) << value << " = "; 30 31 // display bits 32 for ( unsigned i = 1; i <= SHIFT + 1; i++ ) 33 { 34 cout << ( value & MASK ? '1' : '0' ); 35 value <<= 1; // shift value left by 1 36 37 if ( i % 8 == 0 ) // output a space after 8 bits 38 cout << ' '; 39 } // end for 40 41 cout << endl; 42 } // end function displayBits
|
Function displayBits (lines 2442) uses the bitwise AND operator to combine variable value with constant MASK. Often, the bitwise AND operator is used with an operand called a maskan integer value with specific bits set to 1. Masks are used to hide some bits in a value while selecting other bits. In displayBits, line 27 assigns constant MASK the value 1 << SHIFT. The value of constant SHIFT was calculated in line 26 with the expression
8 * sizeof( unsigned ) - 1
which multiplies the number of bytes an unsigned object requires in memory by 8 (the number of bits in a byte) to get the total number of bits required to store an unsigned object, then subtracts 1. The bit representation of 1 << SHIFT on a computer that represents unsigned objects in four bytes of memory is
10000000 00000000 00000000 00000000
The left-shift operator shifts the value 1 from the low-order (rightmost) bit to the high-order (leftmost) bit in MASK, and fills in 0 bits from the right. Line 34 determines whether a 1 or a 0 should be printed for the current leftmost bit of variable value. Assume that variable value contains 65000 (00000000 00000000 11111101 11101000). When value and MASK are combined using &, all the bits except the high-order bit in variable value are "masked off" (hidden), because any bit "ANDed" with 0 yields 0. If the leftmost bit is 1, value & MASK evaluates to
00000000 00000000 11111101 11101000 (value) 10000000 00000000 00000000 00000000 (MASK) ----------------------------------- 00000000 00000000 00000000 00000000 (value & MASK)
which is interpreted as false, and 0 is printed. Then line 35 shifts variable value left by one bit with the expression value <<= 1 (i.e., value = value << 1). These steps are repeated for each bit variable value. Eventually, a bit with a value of 1 is shifted into the leftmost bit position, and the bit manipulation is as follows:
11111101 11101000 00000000 00000000 (value) 10000000 00000000 00000000 00000000 (MASK) ----------------------------------- 10000000 00000000 00000000 00000000 (value & MASK)
Because both left bits are 1s, the result of the expression is nonzero (true) and a value of 1 is printed. Figure 22.7 summarizes the results of combining two bits with the bitwise AND operator.
Bit 1 |
Bit 2 |
Bit 1 & Bit 2 |
---|---|---|
0 |
0 |
0 |
1 |
0 |
0 |
0 |
1 |
0 |
1 |
1 |
1 |
Common Programming Error 22.3
Using the logical AND operator (&&) for the bitwise AND operator (&) and vice versa is a logic error. |
The program of Fig. 22.8 demonstrates the bitwise AND operator, the bitwise inclusive OR operator, the bitwise exclusive OR operator and the bitwise complement operator. Function displayBits (lines 5775) prints the unsigned integer values.
Figure 22.8. Bitwise AND, bitwise inclusive-OR, bitwise exclusive-OR and bitwise complement operators.
(This item is displayed on pages 1069 - 1070 in the print version)
1 // Fig. 22.8: fig22_08.cpp 2 // Using the bitwise AND, bitwise inclusive OR, bitwise 3 // exclusive OR and bitwise complement operators. 4 #include 5 using std::cout; 6 7 #include 8 using std::endl; 9 using std::setw; 10 11 void displayBits( unsigned ); // prototype 12 13 int main() 14 { 15 unsigned number1; 16 unsigned number2; 17 unsigned mask; 18 unsigned setBits; 19 20 // demonstrate bitwise & 21 number1 = 2179876355; 22 mask = 1; 23 cout << "The result of combining the following "; 24 displayBits( number1 ); 25 displayBits( mask ); 26 cout << "using the bitwise AND operator & is "; 27 displayBits( number1 & mask ); 28 29 // demonstrate bitwise | 30 number1 = 15; 31 setBits = 241; 32 cout << " The result of combining the following "; 33 displayBits( number1 ); 34 displayBits( setBits ); 35 cout << "using the bitwise inclusive OR operator | is "; 36 displayBits( number | setbits ); 37 38 // demonstrate bitwise exclusive OR 39 number1 = 139; 40 number2 = 199; 41 cout << " The result of combining the following "; 42 displayBits( number1 ); 43 displayBits( number2 ); 44 cout << "using the bitwise exclusive OR operator ^ is "; 45 displayBits( number ^ number2 ); 46 47 // demonstrate bitwise complement 48 number1 = 21845; 49 cout << " The one's complement of "; 50 displayBits( number1 ); 51 cout << "is" << endl; 52 displayBits( ~number1 ); 53 return 0; 54 } // end main 55 56 // display bits of an unsigned integer value 57 void displayBits( unsigned value ) 58 { 59 const int SHIFT = 8 * sizeof( unsigned ) - 1; 60 const unsigned MASK = 1 << SHIFT; 61 62 cout << setw( 10 ) << value << " = "; 63 64 // display bits 65 for ( unsigned i = 1; i <= SHIFT + 1; i++ ) 66 { 67 cout << ( value & MASK ? '1' : '0' ); 68 value <<= 1; // shift value left by 1 69 70 if ( i % 8 == 0 ) // output a space after 8 bits 71 cout << ' '; 72 } // end for 73 74 cout << endl; 75 } // end function displayBits
|
Bitwise AND Operator (&)
In Fig. 22.8, line 21 assigns 2179876355 (10000001 11101110 01000110 00000011) to variable number1, and line 22 assigns 1 (00000000 00000000 00000000 00000001) to variable mask. When mask and number1 are combined using the bitwise AND operator (&) in the expression number1 & mask (line 27), the result is 00000000 00000000 00000000 00000001. All the bits except the low-order bit in variable number1 are "masked off" (hidden) by "ANDing" with constant MASK.
Bitwise Inclusive OR Operator (|)
The bitwise inclusive-OR operator is used to set specific bits to 1 in an operand. In Fig. 22.8, line 30 assigns 15 (00000000 00000000 00000000 00001111) to variable number1, and line 31 assigns 241 (00000000 00000000 00000000 11110001) to variable setBits. When number1 and setBits are combined using the bitwise OR operator in the expression number1 | setBits (line 36), the result is 255 (00000000 00000000 00000000 11111111). Figure 22.9 summarizes the results of combining two bits with the bitwise inclusive-OR operator.
Bit 1 |
Bit 2 |
Bit 1 | Bit 2 |
---|---|---|
0 |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
1 |
1 |
1 |
Common Programming Error 22.4
Using the logical OR operator (||) for the bitwise OR operator (|) and vice versa is a logic error. |
Bitwise Exclusive OR (^)
The bitwise exclusive OR operator (^) sets each bit in the result to 1 if exactly one of the corresponding bits in its two operands is 1. In Fig. 22.8, lines 3940 assign variables number1 and number2 the values 139 (00000000 00000000 00000000 10001011) and 199 (00000000 00000000 00000000 11000111), respectively. When these variables are combined with the exclusive-OR operator in the expression number1 ^ number2 (line 45), the result is 00000000 00000000 00000000 01001100. Figure 22.10 summarizes the results of combining two bits with the bitwise exclusive-OR operator.
Bit 1 |
Bit 2 |
Bit 1 ^ Bit 2 |
---|---|---|
0 |
0 |
0 |
1 |
0 |
1 |
0 |
1 |
1 |
1 |
1 |
0 |
Bitwise Complement (~)
The bitwise complement operator (~) sets all 1 bits in its operand to 0 in the result and sets all 0 bits to 1 in the resultotherwise referred to as "taking the one's complement of the value." In Fig. 22.8, line 48 assigns variable number1 the value 21845 (00000000 00000000 01010101 01010101). When the expression ~number1 evaluates, the result is (11111111 11111111 10101010 10101010).
Figure 22.11 demonstrates the left-shift operator (<<) and the right-shift operator (>>). Function displayBits (lines 3149) prints the unsigned integer values.
Figure 22.11. Bitwise shift operators.
(This item is displayed on pages 1072 - 1073 in the print version)
1 // Fig. 22.11: fig22_11.cpp 2 // Using the bitwise shift operators. 3 #include 4 using std::cout; 5 using std::endl; 6 7 #include 8 using std::setw; 9 10 void displayBits( unsigned ); // prototype 11 12 int main() 13 { 14 unsigned number1 = 960; 15 16 // demonstrate bitwise left shift 17 cout << "The result of left shifting "; 18 displayBits( number1 ); 19 cout << "8 bit positions using the left-shift operator is "; 20 displayBits( number1 << 8 ); 21 22 // demonstrate bitwise right shift 23 cout << " The result of right shifting "; 24 displayBits( number1 ); 25 cout << "8 bit positions using the right-shift operator is "; 26 displayBits( number1 >> 8 ); 27 return 0; 28 } // end main 29 30 // display bits of an unsigned integer value 31 void displayBits( unsigned value ) 32 { 33 const int SHIFT = 8 * sizeof( unsigned ) - 1; 34 const unsigned MASK = 1 << SHIFT; 35 36 cout << setw( 10 ) << value << " = "; 37 38 // display bits 39 for ( unsigned i = 1; i <= SHIFT + 1; i++ ) 40 { 41 cout << ( value & MASK ? '1' : '0' ); 42 value <<= 1; // shift value left by 1 43 44 if ( i % 8 == 0 ) // output a space after 8 bits 45 cout << ' '; 46 } // end for 47 48 cout << endl; 49 } // end function displayBits
|
Left-Shift Operator
The left-shift operator (<<) shifts the bits of its left operand to the left by the number of bits specified in its right operand. Bits vacated to the right are replaced with 0s; bits shifted off the left are lost. In the program of Fig. 22.11, line 14 assigns variable number1 the value 960 (00000000 00000000 00000011 11000000). The result of left-shifting variable number1 8 bits in the expression number1 << 8 (line 20) is 245760 (00000000 00000011 11000000 00000000).
Right-Shift Operator
The right-shift operator (>>) shifts the bits of its left operand to the right by the number of bits specified in its right operand. Performing a right shift on an unsigned integer causes the vacated bits at the left to be replaced by 0s; bits shifted off the right are lost. In the program of Fig. 22.11, the result of right-shifting number1 in the expression number1 >> 8 (line 26) is 3 (00000000 00000000 00000000 00000011).
Common Programming Error 22.5
The result of shifting a value is undefined if the right operand is negative or if the right operand is greater than or equal to the number of bits in which the left operand is stored. |
Portability Tip 22.4
The result of right-shifting a signed value is machine dependent. Some machines fill with zeros and others use the sign bit. |
Bitwise Assignment Operators
Each bitwise operator (except the bitwise complement operator) has a corresponding assignment operator. These bitwise assignment operators are shown in Fig. 22.12; they are used in a similar manner to the arithmetic assignment operators introduced in Chapter 2.
Bitwise assignment operators |
|
---|---|
&= |
Bitwise AND assignment operator. |
|= |
Bitwise inclusive-OR assignment operator. |
^= |
Bitwise exclusive-OR assignment operator. |
<<= |
Left-shift assignment operator. |
>>= |
Right-shift with sign extension assignment operator. |
Figure 22.13 shows the precedence and associativity of the operators introduced up to this point in the text. They are shown top to bottom in decreasing order of precedence.
Operators |
Associativity |
Type |
||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|
:: (unary; right to left) |
:: (binary; left to right) |
left to right |
highest |
|||||||||
() |
[] |
. |
-> |
++ |
-- |
static_cast< type >() |
left to right |
unary |
||||
++ |
-- |
+ |
- |
! |
delete sizeof |
right to left |
unary |
|||||
* |
~ |
& |
new |
|||||||||
* |
/ |
% |
left to right |
multiplicative |
||||||||
+ |
- |
left to right |
additive |
|||||||||
<< |
>> |
left to right |
shifting |
|||||||||
< |
<= |
> |
>= |
left to right |
relational |
|||||||
== |
!= |
left to right |
equality |
|||||||||
& |
left to right |
bitwise AND |
||||||||||
^ |
left to right |
bitwise XOR |
||||||||||
| |
left to right |
bitwise OR |
||||||||||
&& |
left to right |
logical AND |
||||||||||
|| |
left to right |
logical OR |
||||||||||
?: |
right to left |
conditional |
||||||||||
= |
+= |
-= |
*= |
/= |
%= |
&= |
|= |
^= |
<<= |
>>= |
right to left |
assignment |
, |
left to right |
comma |
Introduction to Computers, the Internet and World Wide Web
Introduction to C++ Programming
Introduction to Classes and Objects
Control Statements: Part 1
Control Statements: Part 2
Functions and an Introduction to Recursion
Arrays and Vectors
Pointers and Pointer-Based Strings
Classes: A Deeper Look, Part 1
Classes: A Deeper Look, Part 2
Operator Overloading; String and Array Objects
Object-Oriented Programming: Inheritance
Object-Oriented Programming: Polymorphism
Templates
Stream Input/Output
Exception Handling
File Processing
Class string and String Stream Processing
Web Programming
Searching and Sorting
Data Structures
Bits, Characters, C-Strings and structs
Standard Template Library (STL)
Other Topics
Appendix A. Operator Precedence and Associativity Chart
Appendix B. ASCII Character Set
Appendix C. Fundamental Types
Appendix D. Number Systems
Appendix E. C Legacy Code Topics
Appendix F. Preprocessor
Appendix G. ATM Case Study Code
Appendix H. UML 2: Additional Diagram Types
Appendix I. C++ Internet and Web Resources
Appendix J. Introduction to XHTML
Appendix K. XHTML Special Characters
Appendix L. Using the Visual Studio .NET Debugger
Appendix M. Using the GNU C++ Debugger
Bibliography