22.5 Bitwise Operators

C++ provides extensive bit-manipulation capabilities for getting down to the so-called “bits-and-bytes” level. Operating systems, test-equipment software, networking software and many other kinds of software require that you communicate “directly with the hardware.” We introduce each of the 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 eight bits, each of which forms a byte—the 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). Normally the bitwise operators are used with unsigned integers.

Portability Tip 22.2

Bitwise data manipulations are machine dependent.

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. 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 (|), bitwise exclusive OR (^), left shift (<<), right shift (>>) and bitwise complement (~)—also known as the one’s complement. We’ve 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 operand—but not both—is 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.

Fig. 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’s 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.

Fig. 22.6 Printing an unsigned integer in bits.

Alternate View

 1  // Fig. 22.6: fig22_06.cpp
 2  // Printing an unsigned integer in bits.
 3  #include <iostream>
 4  #include <iomanip>
 5  using namespace std;
 6
 7  void displayBits(unsigned); // prototype
 8
 9  int main() {
10     unsigned inputValue{0}; // integral value to print in binary
11
12     cout << "Enter an unsigned integer: ";
13     cin >> inputValue;
14     displayBits(inputValue);
15  }
16
17  // display bits of an unsigned integer value
18  void displayBits(unsigned value) {
19     const unsigned SHIFT{8 * sizeof(unsigned) - 1};              
20     const unsigned MASK{static_cast<const unsigned>(1 << SHIFT)};
21
22     cout << setw(10) << value << " = ";
23
24     // display bits
25     for (unsigned i{1}; i <= SHIFT + 1; ++i) {
26        cout << (value & MASK ? '1' : '0');  
27        value <<= 1; // shift value left by 1
28
29        if (i % 8 == 0) { // output a space after 8 bits
30           cout << ' ';
31        }
32     }
33
34     cout << endl;
35  }

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

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

Function displayBits (lines 18–35) uses the bitwise AND operator to combine variable value with constant MASK. Often, the bitwise AND operator is used with an operand called a mask—an 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 20 initializes constant MASK with 1 << SHIFT. The value of constant SHIFT was calculated in line 19 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 26 prints a 1 or a 0 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 27 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 expression’s result is nonzero (true) and 1 is printed. Figure 22.7 summarizes the results of combining two bits with the bitwise AND operator.

Fig. 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.2

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 47–64) prints the unsigned integer values.

Fig. 22.8 Bitwise AND, inclusive OR, exclusive OR and complement operators.

Alternate View

 1  // Fig. 22.8: fig22_08.cpp
 2  // Bitwise AND, inclusive OR,
 3  // exclusive OR and complement operators.
 4  #include <iostream>
 5  #include <iomanip>
 6  using namespace std;
 7
 8  void displayBits(unsigned); // prototype
 9
10  int main() {
11     // demonstrate bitwise &
12     unsigned number1{2179876355};
13     unsigned mask{1};
14     cout << "The result of combining the following
";
15     displayBits(number1);
16     displayBits(mask);
17     cout << "using the bitwise AND operator & is
";
18     displayBits(number1 & mask);
19
20     // demonstrate bitwise |
21     number1 = 15;
22     unsigned setBits{241};
23     cout << "
The result of combining the following
";
24     displayBits(number1);
25     displayBits(setBits);
26     cout << "using the bitwise inclusive OR operator | is
";
27     displayBits(number1 | setBits);
28
29     // demonstrate bitwise exclusive OR
30     number1 = 139;
31     unsigned number2{199};
32     cout << "
The result of combining the following
";
33     displayBits(number1);
34     displayBits(number2);
35     cout << "using the bitwise exclusive OR operator ^ is
";
36     displayBits(number1 ^ number2);
37
38     // demonstrate bitwise complement
39     number1 = 21845;
40     cout << "
The one's complement of
";
41     displayBits(number1);
42     cout << "is" << endl;
43     displayBits(~number1);
44  }
45
46  // display bits of an unsigned integer value
47  void displayBits(unsigned value) {
48     const unsigned SHIFT{8 * sizeof(unsigned) - 1};
49     const unsigned MASK{static_cast<const unsigned>(1 << SHIFT)};
50
51     cout << setw(10) << value << " = ";
52
53     // display bits
54     for (unsigned i{1}; i <= SHIFT + 1; ++i) {
55        cout << (value & MASK ? '1' : '0');
56        value <<= 1; // shift value left by 1
57
58        if (i % 8 == 0) { // output a space after 8 bits
59           cout << ' ';
60        }
61     }
62
63     cout << endl;
64  }

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 12 uses 2179876355 (10000001 11101110 01000110 00000011) to initialize variable number1, and line 14 uses 1 (00000000 00000000 00000000 00000001) to initialize variable mask. When mask and number1 are combined using the bitwise AND operator (&) in the expression number1 & mask (line 18), 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 21 assigns 15 (00000000 00000000 00000000 00001111) to variable number1, and line 22 uses 241 (00000000 00000000 00000000 11110001) to initialize variable setBits. When number1 and setBits are combined using the bitwise inclusive OR operator in the expression number1 | setBits (line 27), the result is 255 (00000000 00000000 00000000 11111111). Figure 22.9 summarizes the results of combining two bits with the bitwise inclusive-OR operator.

Common Programming Error 22.3

Using the logical OR operator (||) for the bitwise OR operator (|) and vice versa is a logic error.

Fig. 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

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 30–31 give 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 bitwise exclusive OR operator in the expression number1 ^ number2 (line 36), the result is 00000000 00000000 00000000 01001100. Figure 22.10 summarizes the results of combining two bits with the bitwise exclusive OR operator.

Fig. 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 result—otherwise referred to as “taking the one’s complement of the value.” In Fig. 22.8, line 39 assigns variable number1 the value 21845 (00000000 00000000 01010101 01010101). When the expression ~number1 evaluates, the result is (11111111 11111111 10101010 10101010).

Bitwise Shift Operators

Figure 22.11 demonstrates the left-shift operator (<<) and the right-shift operator (>>). Function displayBits (lines 26–43) prints the unsigned integer values.

Fig. 22.11 Bitwise shift operators.

Alternate View

 1  // Fig. 22.11: fig22_11.cpp
 2  // Using the bitwise shift operators.
 3  #include <iostream>
 4  #include <iomanip>
 5  using namespace std;
 6
 7  void displayBits(unsigned); // prototype
 8
 9  int main() {
10     unsigned number1{960};
11
12     // demonstrate bitwise left shift
13     cout << "The result of left shifting
";
14     displayBits(number1);
15     cout << "8 bit positions using the left-shift operator is
";
16     displayBits(number1 << 8);
17
18     // demonstrate bitwise right shift
19     cout << "
The result of right shifting
";
20     displayBits(number1);
21     cout << "8 bit positions using the right-shift operator is
";
22     displayBits(number1 >> 8);
23  }
24
25  // display bits of an unsigned integer value
26  void displayBits(unsigned value) {
27     const unsigned SHIFT{8 * sizeof(unsigned) - 1};
28     const unsigned MASK{static_cast<const unsigned>(1 << SHIFT)};
29
30     cout << setw(10) << value << " = ";
31
32     // display bits
33     for (unsigned i{1}; i <= SHIFT + 1; ++i) {
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        }
40     }
41
42     cout << endl;
43  }

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 Fig. 22.11, line 10 initializes variable number1 with the value 960 (00000000 00000000 00000011 11000000). The result of left-shifting variable number1 eight bits in the expression number1 << 8 (line 16) 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 22) is 3 (00000000 00000000 00000000 00000011).

Common Programming Error 22.4

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.3

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’re used in a similar manner to the arithmetic assignment operators introduced in Chapter 4.

Fig. 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.

Operator Precedence

Figure 22.13 shows the precedence and associativity of the operators introduced up to this point in the text. They’re shown top to bottom in decreasing order of precedence.

Fig. 22.13 Operator precedence and associativity.

Operators Associativity Type
:: (unary; right to left) left to right primary
:: (binary; left to right)
() (grouping parentheses)
[See caution in Fig. 2.10 regarding grouping parentheses.]  
() [] . -> ++ -- static_cast<type>() left to right postfix
++ -- + - ! delete sizeof
     * ~ & new
right to left prefix
* / % 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

C++14 Binary Literals

As of C++14, you may now include binary literals in your source code. To do so, precede a sequence of 1s and 0s with 0b or 0B. For example, you can define a 32-bit mask like the one we used in the displayBits functions of this section’s examples as


0b10000000'00000000'00000000'00000000

The preceding literal also uses C++14’s single-quote character to separate groups of digits for readability.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset