Bitwise Operators

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.


Figure 22.5. Bitwise operators.

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  

Enter an unsigned integer: 65000 65000 = 00000000 00000000 11111101 11101000  

 

Enter an unsigned integer: 29 29 = 00000000 00000000 00000000 00011101  


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.

Figure 22.7. 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  

The result of combining the following 2179876355 = 10000001 11101110 01000110 00000011 1 = 00000000 00000000 00000000 00000001 using the bitwise AND operator & is 1 = 00000000 00000000 00000000 00000001 The result of combining the following 15 = 00000000 00000000 00000000 00001111 241 = 00000000 00000000 00000000 11110001 using the bitwise inclusive OR operator | is 255 = 00000000 00000000 00000000 11111111 The result of combining the following 139 = 00000000 00000000 00000000 10001011 199 = 00000000 00000000 00000000 11000111 using the bitwise exclusive OR operator ^ is 76 = 00000000 00000000 00000000 01001100 The one's complement of 21845 = 00000000 00000000 01010101 01010101 is 4294945450 = 11111111 11111111 10101010 10101010  

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.


Figure 22.9. 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.

Figure 22.10. 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  

The result of left shifting 960 = 00000000 00000000 00000011 11000000 8 bit positions using the left-shift operator is 245760 = 00000000 00000011 11000000 00000000 The result of right shifting 960 = 00000000 00000000 00000011 11000000 8 bit positions using the right-shift operator is 3 = 00000000 00000000 00000000 00000011  


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.

Figure 22.12. Bitwise assignment operators.

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.

Figure 22.13. Operator precedence and associativity.

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

Категории