Skip to content

4.8. The Bitwise Operators

The bitwise operators take operands of integral type that they use as a collection of bits. These operators let us test and set individual bits. As we’ll see in § 17.2 (p. 723), we can also use these operators on a library type named bitset that represents a flexibly sized collection of bits.

As usual, if an operand is a “small integer,” its value is first promoted (§ 4.11.1, p. 160) to a larger integral type. The operand(s) can be either signed or unsigned.

Table 4.3. Bitwise Operators (Left Associative)

OperatorFunctionUse
<<Left shiftexpr1 << expr2
>>Right shiftexpr1 >> expr2
~Bitwise NOT~expr
&Bitwise ANDexpr1 & expr2
|Bitwise ORexpr1 | expr2
^Bitwise XORexpr1 ^ expr2

If the operand is signed and its value is negative, then the way that the “sign bit” is handled in a number of the bitwise operations is machine dependent. Moreover, doing a left shift that changes the value of the sign bit is undefined.

WARNING

Because there are no guarantees for how the sign bit is handled, we strongly recommend using unsigned types with the bitwise operators.

Bitwise Shift Operators

We have already used the overloaded versions of the >> and << operators that the IO library defines to do input and output. The built-in meaning of these operators is that they perform a bitwise shift on their operands. They yield a value that is a copy of the (possibly promoted) left-hand operand with the bits shifted as directed by the right-hand operand. The right-hand operand must not be negative and must be a value that is strictly less than the number of bits in the result. Otherwise, the operation is undefined. The bits are shifted left (<<) or right (>>). Bits that are shifted off the end are discarded:

c++
// The illustrations have the low-order bit on the right
// These examples assume char has 8 bits, and int has 32

unsigned char bits = 0233;
// 10011011
// 0233 is an octal literal (§ 2.1.3, p.38)

bits << 8;
// 00000000 00000000 10011011 00000000
// bits promoted to int and then shifted left by 8 bits

bits << 31;
// 10000000 00000000 00000000 00000000
// left shift 31 bits, left-most bits discarded

bits >> 3;
// 00000000 00000000 00000000 00010011
// Right shift 3 bits, 3 right-most bits discarded

The left-shift operator (the <<operator) inserts 0-valued bits on the right. The behavior of the right-shift operator (the >>operator) depends on the type of the left-hand operand: If that operand is unsigned, then the operator inserts 0-valued bits on the left; if it is a signed type, the result is implementation defined—either copies of the sign bit or 0-valued bits are inserted on the left.

Bitwise NOT Operator

The bitwise NOT operator (the ~operator) 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:

c++
unsigned char bits = 0227;         // 10010111
~bits;  // 11111111 11111111 11111111 01101000

Here, our char operand is first promoted to int. Promoting a char to int leaves the value unchanged but adds 0 bits to the high order positions. Thus, promoting bits to int adds 24 high order bits, all of which are 0-valued. The bits in the promoted value are inverted.

Bitwise AND, OR, and XOR Operators

The AND (&), OR (|), and XOR (^) operators generate new values with the bit pattern composed from its two operands:

c++
unsigned char b1 = 0145;               // 01100101
unsigned char b2 = 0257;               // 10101111

b1 & b2;    // 00000000 00000000 00000000 00100101
b1 | b2;    // 00000000 00000000 00000000 11101111
b1 ^ b2;    // 00000000 00000000 00000000 11001010

For each bit position in the result of the bitwise AND operator (the &operator) the bit is 1 if both operands contain 1; otherwise, the result is 0. For the OR (inclusive or) operator (the |operator), the bit is 1 if either or both operands contain 1; otherwise, the result is 0. For the XOR (exclusive or) operator (the ^operator), the bit is 1 if either but not both operands contain 1; otherwise, the result is 0.

WARNING

It is a common error to confuse the bitwise and logical operators (§ 4.3, p. 141). For example to confuse the bitwise & with the logical &&, the bitwise | with the logical ||, and the bitwise ~ and the logical !).

Using Bitwise Operators

As an example of using the bitwise operators let’s assume 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 an unsigned integral value:

c++
unsigned long quiz1 = 0; // we'll use this value as a collection of bits

We define quiz1 as an unsigned long. Thus, quiz1 will have at least 32 bits on any machine. We explicitly initialize 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, we’d like to be able to set the bit corresponding to student number 27 to indicate that this student passed the quiz. We can indicate that student number 27 passed by creating a value that has only bit 27 turned on. If we then bitwise OR that value with quiz1, all the bits except bit 27 will remain unchanged.

For the purpose of this example, we will count the bits of quiz1 by assigning 0 to the low-order bit, 1 to the next bit, and so on.

We can obtain a value indicating that student 27 passed by using the left-shift operator and an unsigned long integer literal 1 (§ 2.1.3, p. 38):

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

1UL has a 1 in the low-order bit and (at least) 31 zero bits. We specified unsigned long because ints are only guaranteed to have 16 bits, and we need at least 17. This expression shifts the 1 bit left 27 positions inserting 0 bits behind it.

Next we OR this value with quiz1. Because we want to update the value of quiz1, we use a compound assignment (§ 4.4, p. 147):

c++
quiz1 |= 1UL << 27; // indicate student number 27 passed

The |= operator executes analogously to how += does. It is equivalent to

c++
quiz1 = quiz1 | 1UL << 27; // equivalent to 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. This time we need an integer that has bit 27 turned off and all the other bits turned on. We’ll bitwise AND this value with quiz1 to turn off just that bit:

c++
quiz1 &= ~(1UL << 27); // student number 27 failed

We obtain a value with all but bit 27 turned on by inverting our previous value. That value had 0 bits in all but bit 27, which was a 1. Applying the bitwise NOT to that value will turn off bit 27 and turn on all the others. When we bitwise AND this value with quiz1, all except bit 27 will remain unchanged.

Finally, we might want to know how the student at position 27 fared:

c++
bool status = quiz1 & (1UL << 27); // how did student number 27 do?

Here we AND a value that has bit 27 turned on with quiz1. The result is nonzero (i.e., true) if bit 27 of quiz1 is also on; otherwise, it evaluates to zero.

Shift Operators (aka IO Operators) Are Left Associative

Fundamental

Although many programmers never use the bitwise operators directly, most programmers do use overloaded versions of these operators for IO. An overloaded operator has the same precedence and associativity as the built-in version of that operator. Therefore, programmers need to understand the precedence and associativity of the shift operators even if they never use them with their built-in meaning.

Because the shift operators are left associative, the expression

c++
cout << "hi" << " there" << endl;

executes as

c++
( (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 with the third.

The shift operators have midlevel precedence: lower than the arithmetic operators but higher than the relational, assignment, and conditional operators. These relative precedence levels mean we usually have to use parentheses to force the correct grouping of operators with lower precedence.

c++
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 last cout is interpreted as

c++
(cout << 10) < 42;

which says to “write 10 onto cout and then compare the result of that operation (i.e., cout) to 42.”

INFO

Exercises Section 4.8

Exercise 4.25: What is the value of ~'q' << 6 on a machine with 32-bit ints and 8 bit chars, that uses Latin-1 character set in which 'q' has the bit pattern 01110001?

Exercise 4.26: In our grading example in this section, what would happen if we used unsigned int as the type for quiz1?

Exercise 4.27: What is the result of each of these expressions?

c++
unsigned long ul1 = 3, ul2 = 7;

(a)ul1 & ul2

(b)ul1 | ul2

(c)ul1 && ul2

(d)ul1 || ul2