Section 5.3. The Bitwise Operators


5.3. The Bitwise Operators

The bitwise operators take operands of integral type. These operators treat their integral operands as a collection of bits, providing operations to test and set individual bits. In addition, these operators may be applied to bitset (Section 3.5, p. 101) operands with the behavior as described here for integral operands.

Table 5.3. Bitwise Operators

Operator

Function

Use

~

bitwise NOT

~expr

<<

left shift

expr1 << expr2

>>

right shift

expr1 >> expr2

&

bitwise AND

expr1 & expr2

^

bitwise XOR

expr1 ^ expr2

|

bitwise OR

expr1 | expr2


The type of an integer manipulated by the bitwise operators can be either signed or unsigned. If the value is negative, then the way that the "sign bit" is handled in a number of the bitwise operations is machine-dependent. It is, therefore, likely to differ across implementations; programs that work under one implementation may fail under another.

Because there are no guarantees for how the sign bit is handled, we strongly recommend using an unsigned type when using an integral value with the bitwise operators.



In the following examples we assume that an unsigned char has 8 bits. The bitwise NOT operator (~) is similar in behavior to the bitset flip (Section 3.5.2, p. 105) operation: It generates a new value with the bits of its operand inverted. Each 1 bit is set to 0; each 0 bit is set to 1:

unsigned char bits = 0227;

bits = ~bits;


The <<, >> operators are the bitwise shift operators. These operators use their right-hand operand to indicate by how many bits to shift. They yield a value that is a copy of the left-hand operand with the bits shifted as directed by the right-hand operand. The bits are shifted left (<<) or right (>>), discarding the bits that are shifted off the end.

unsigned char bits = 1;

bits << 1; // left shift

bits << 2; // left shift

bits >> 3; // right shift


The left shift operator (<<) inserts 0-valued bits in from the right. The right shift operator (>>) inserts 0-valued bits in from the left if the operand is unsigned. If the operand is signed, it can either insert copies of the sign bit or insert 0-valued bits; which one it uses is implementation defined. The right-hand operand must not be negative and must be a value that is strictly less than the number of bits in the left-hand operand. Otherwise, the effect of the operation is undefined.

The bitwise AND operator (&) takes two integral operands. For each bit position, the result is 1 if both operands contain 1; otherwise, the result is 0.

It is a common error to confuse the bitwise AND operator (&) with the logical AND operator (&&) (Section 5.2, p. 152). Similarly, it is common to confuse the bitwise OR operator (|) and the logical OR operator(||).



Here we illustrate the result of bitwise AND of two unsigned char values, each of which is initialized by an octal literal:

unsigned char b1 = 0145;

unsigned char b2 = 0257;

unsigned char result = b1 & b2;


The bitwise XOR (exclusive or) operator (^) also takes two integral operands. For each bit position, the result is 1 if either but not both operands contain 1; otherwise, the result is 0.

result = b1 ^ b2;


The bitwise OR (inclusive or) operator (|) takes two integral operands. For each bit position, the result is 1 if either or both operands contain 1; otherwise, the result is 0.

result = b1 | b2;


5.3.1. Using bitset Objects or Integral Values

We said that the bitset class was easier to use than the lower-level bitwise operations on integral values. Let's look at a simple example and show how we might solve a problem using either a bitset or the bitwise operators. Assume that a teacher has 30 students in a class. Each week the class is given a pass/fail quiz. We'll track the results of each quiz using one bit per student to represent the pass or fail grade on a given test. We might represent each quiz in either a bitset or as an integral value:

      bitset<30> bitset_quiz1;     //  bitset solution      unsigned long int_quiz1 = 0; // simulated collection of bits 

In the bitset case we can define bitset_quiz1 to be exactly the size we need. By default each of the bits is set to zero. In the case where we use a built-in type to hold our quiz results, we define int_quiz1 as an unsigned long, meaning that it will have at least 32 bits on any machine. Finally, we explicitly initialize int_quiz1 to ensure that the bits start out with well-defined values.

The teacher must be able to set and test individual bits. For example, assuming that the student represented by position 27 passed, we'd like to be able to set that bit appropriately:

      bitset_quiz1.set(27);   //  indicate student number 27 passed      int_quiz1 |= 1UL<<27;   //  indicate student number 27 passed 

In the bitset case we do so directly by passing the bit we want turned on to set. The unsigned long case will take a bit more explanation. The way we'll set a specific bit is to OR our quiz data with another integer that has only one bitthe one we wantturned on. That is, we need an unsigned long where bit 27 is a one and all the other bits are zero. We can obtain such a value by using the left shift operator and the integer constant 1:

      1UL << 27;  //  generate a value with only bit number 27 set 

Now when we bitwise OR this value with int_quiz1, all the bits except bit 27 will remain unchanged. That bit will be turned on. We use a compound assignment (Section 1.4.1, p. 13) to OR this value into int_quiz1. This operator, |=, executes in the same way that += does. It is equivalent to the more verbose:

      //  following assignment is equivalent to int_quiz1 |= 1UL << 27;      int_quiz1 = int_quiz1 | 1UL << 27; 

Imagine that the teacher reexamined the quiz and discovered that student 27 actually had failed the test. The teacher must now turn off bit 27:

      bitset_quiz1.reset(27);   // student number 27 failed      int_quiz1 &= ~(1UL<<27);  // student number 27 failed 

Again, the bitset version is direct. We reset the indicated bit. For the simulated case, we need to do the inverse of what we did to set the bit: This time we'll need an integer that has bit 27 turned off and all the other bits turned on. We'll bitwise AND this value with our quiz data to turn off just that bit. We can obtain a value with all but bit 27 turned on by inverting our previous value. Applying the bitwise NOT to the previous integer will turn on every bit except the 27th. When we bitwise AND this value with int_quiz1, all except bit 27 will remain unchanged.

Finally, we might want to know how the student at position 27 fared. To do so, we could write

      bool status;      status = bitset_quiz1[27];       // how did student number 27 do?      status = int_quiz1 & (1UL<<27);  // how did student number 27 do? 

In the bitset case we can fetch the value directly to determine how that student did. In the unsigned long case, the first step is to set the 27th bit of an integer to 1. The bitwise AND of this value with int_quiz1 evaluates to nonzero if bit 27 of int_quiz1 is also on; otherwise, it evaluates to zero.

In general, the library bitset operations are more direct, easier to read, easier to write, and more likely to be used correctly. Moreover, the size of a bitset is not limited by the number of bits in an unsigned. Ordinarily bitset should be used in preference to lower-level direct bit manipulation of integral values.



Exercises Section 5.3.1

Exercise 5.9:

Assume the following two definitions:

      unsigned long ul1 = 3, ul2 = 7; 

What is the result of each of the following expressions?

      (a) ul1 & ul2     (c)  ul1 | ul2      (b) ul1 && ul2    (d)  ul1 || ul2 

Exercise 5.10:

Rewrite the bitset expressions that set and reset the quiz results using a subscript operator.


5.3.2. Using the Shift Operators for IO

The IO library redefines the bitwise >> and << operators to do input and output. Even though many programmers never need to use the bitwise operators directly, most programs do make extensive use of the overloaded versions of these operators for IO. When we use an overloaded operator, it has the same precedence and associativity as is defined for the built-in version of the operator. Therefore, programmers need to understand the precedence and associativity of these operators even if they never use them with their built-in meaning as the shift operators.

The IO Operators Are Left Associative

Like the other binary operators, the shift operators are left associative. These operators group from left to right, which accounts for the fact that we can concatenate input and output operations into a single statement:

      cout << "hi" << " there" << endl; 

executes as:

      ( (cout << "hi") << " there" ) << endl; 

In this statement, the operand "hi" is grouped with the first << symbol. Its result is grouped with the second, and then that result is grouped to the third.

The shift operators have midlevel precedence: lower precedence than the arithmetic operators but higher than the relational, assignment, or conditional operators. These relative precedence levels affect how we write IO expressions involving operands that use operators with lower precedence. We often need to use parentheses to force the right grouping:

      cout << 42 + 10;   // ok, + has higher precedence, so the sum is printed      cout << (10 < 42); // ok: parentheses force intended grouping; prints 1      cout << 10 < 42;   // error: attempt to compare cout to 42! 

The second cout is interpreted as

      (cout << 10) < 42; 

this expression says to "write 10 onto cout and then compare the result of that operation (e.g., cout) to 42."



C++ Primer
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2006
Pages: 223
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net