# 2.11. Supplemental: Bitwise Operators

Some courses, like those preparing students for specialized topics like embedded programming or writing device drivers, often cover the bitwise operators in detail, but introductory C++ courses generally do not. Furthermore, providing realistic examples also involves programming structures not introduced until the next chapter. Still, we briefly cover bitwise operators here for completeness and provide a consistent location for later reference and review.

Unless your instructor explicitly assigns this section, you may skip it for now and return later when these operations are more relevant.

C++ inherits six bitwise operators from C. Bitwise operations are always available in assembly language but are less common in higher-level languages. Including these operators in C made it possible to write operating systems and device drivers in C rather than in assembly. Most bitwise operators require two integer arguments, but complement is a unary operator. Three operators act on the corresponding bits of the two operands; we can conveniently summarize these and the complement operators with truth tables. Two operators treat one operand as a string of bits and shift them to the left or the right. We'll often view one or both operands as a short string of bits for convenience and ease of illustration.

## Basic Bitwise Operators

The basic bitwise operators are simple enough to describe them with simple truth tables. When we use these operators, it's convenient to think about or view both operands in binary: 1s and 0s. Each 0-value corresponds to false, and each 1-value corresponds to true. A simple example follows each truth table, illustrating the meaning of "the bitwise operators operate on the corresponding bits of the two operands." Operating on two bits with a bitwise operator produces a single bit. The following figures detail

• the symbol used for each operator,
• the operator's name,
• an exhaustive list of possible inputs with corresponding outputs,
• a simple rule to summarize the behavior of the operator, and
• a simple, 4-bit example. Although the bitwise operators can operate on integers of any size, the examples rely on 4-bit values simplicity. Apply the operator column by column: the pair of bits in each column above the line are the operands, while the bit below the line is the result.

Viewing the bitwise operations in base-10 is potentially confusing. Nevertheless, they are useful in specialized situations, especially when one operand is a constant, often called a bitmask. Unfortunately, C++ doesn't have a binary number notation, so programmers typically denote integer constants hexadecimal (or occasionally in octal). A single hexadecimal digit corresponds to a nibble (i.e., to 4 bits). So, we can compactly specify each 4-bit cluster as a single hexadecimal digit.

One of the most common uses for bitwise operations is to set, reset, and test bits stored in a multi-bit data structure. Programmers can implement multi-bit data structures in many ways, but they frequently just use integers consisting of 8 to 64 bits, as illustrated in the following examples. We can also implement multi-bit data with arrays (chapter 7), strings (chapter 8), or purpose-built bit-vector classes (chapter 9). Regardless of how we implement them, multi-bit data are often called bit fields, bit sets, bit vectors, bit maps, bit strings, etc.

Programmers use bit fields to represent a variety of settings or conditions compactly. Each bit acts as a switch. A "1" denotes a setting is on, while a "0" means it is off. Programs often use bitwise-OR and bitwise-AND operators in conjunction with constant bit fields, called bitmasks, to switch bits on and off respectively, which corresponds to switching settings on or off.

Input and output operations in a C++ program generally takes place through stream objects (e.g., cin and cout). Programmers control the behaviors of these objects with a set of 1-bit switches or flags. Each bit corresponds to a specific behavior - setting the bit to 0 disables the behavior, and setting it to 1 enables it. Programmers use named bitmasks (i.e., constant values) and bitwise operators to bit settings. Four of the file I/O bitmasks illustrate the concept:

in
Open the file for input (i.e., the program can write to the file)
`in = 0x01 = 00000000000000000000000000000001`
out
Open the file for output (i.e., the program can read from the file)
`out = 0x02 = 00000000000000000000000000000010`
app
Append new data to the end of the file (i.e., preserve existing data in the file)
`app = 0x08 = 00000000000000000000000000001000`
binary
Threat the file contents as binary data (e.g., .gif, .jpg, .wav, .mp3, etc.) files
`binary = 0x20 = 00000000000000000000000000100000`

To create a value that will switch on all four behaviors, a programmer might write an assignment statement such as:

`int mode = in | out | app | binary;`

The bitwise-OR operations produce the bit field: `00000000000000000000000000101011`, which the programmer could then use to open a file that has the combined behaviors.

The library code that opens the file may look similar to the following:

```if (mode & binary != 0)
{
// then configure the file for reading or writing in binary mode
}
if (mode & app != 0)
{
// then configure the file for writing in append mode
}
. . . .```

The bitwise-AND operation switches off all the bits except the one corresponding to the binary bitmask. The magnitude (i.e., the exact value) of `mode & binary` is not significant; what is significant is that the value is not 0. If "binary" had been left out of the assignment statement above (part of the mode calculation), then the expression `mode & binary` would produce a 0, skipping the code for configuring the file for binary I/O.

## Bit-Shift Operators

The two bit-shift operators should look familiar to you, not because we have used them before, but because they are reused as the output and input operators introduced previously. Both operands are integers, and we will continue to view the left-hand operand in binary but will now view the right-hand operand in decimal. Both bit-shift operators treat the left-hand operand as a string of 1s and 0s and shift them left or right by the number of places indicated by the right-hand operand. Shifting may seem confusing but is easy to understand when illustrated with an example.

### Shifting Left

The left shift operator, `<<` moves the bits in an integer to the left. The right-hand operand specifies how many places to shift the bits. For example: Left Shift Operator. Two views of the left shift operator. The shift operation moves an integer's bits to the left. The operation discards the most significant bits, illustrated with strikeout characters, and opens spaces in the least significant positions. The left-hand operand is shown in binary for clarity The right-hand operand, shown in decimal, is the number of places to shift the left operand The high-order bits shifted out on the left are discarded The vacated or opened low-order bits on the right are filled with 0's (highlighted in yellow)

### Shifting Right

The right shift operator, `>>`, is similar to the left shift operator but is a little more complicated. The right shift operator moves the bits in the left-hand operand to the right by the number of places specified by the right-hand operand. The operation shifts the bits out on the right side, discarding them as expected. However, how the operation fills the empty spaces on the left complicates the right shift operator.

Without programmer intervention, the underlying hardware determines how the spaces vacated by the shift are filled. (The ANSI standard calls such features implementation dependent.) Some hardware implements sign extension (i.e., it fills the empty spaces with a copy of the left-most bit), and some hardware does not (i.e., it fills the empty spaces with 0's).

Fortunately, programmers can intervene. In a signed integer (a number capable of storing negative and positive values), the highest-order bit is called the sign bit. Computers generally treat a number as negative when the sign bit is 1 and non-negative (i.e., zero or positive) when it is 0. Negative values are generally not needed when dealing with bit patterns, and so the easy "fix" is defining the integer as `unsigned`. (Using unsigned integers, variables, and constants with all the bitwise operators is common.) When the right shift operator's left operand is unsigned, it always fills the empty spaces on the left with 0s regardless of how the hardware behaves by default.

The following examples demonstrate the right shift operator with and without sign extension: Right Shift Operator. Two views of the right shift operator without sign extension. The shift operation moves an integer's bits to the right. It discards the least significant bits, illustrated with strikeout characters, and opens spaces in the most significant positions. Three cases produce the illustrated results: (a) the left operand is unsigned, (b) the hardware does not perform sign extension, or (c) the original highest-order bit is a 0. The left-hand operand and the result are shown in binary for clarity The right-hand operand, shown in decimal, is the number of places to shift the left operand The low-order bits on the right are discarded The vacated or opened high-order bits on the left are filled with 0's (highlighted in yellow) Right Shift Operator with sign extension. Two views of the right shift operator with sign extension. The shift operation moves an integer's bits to the right. The shift discards the least significant bits, illustrated with strikeout characters, and opens spaces in the most significant positions. However, how the computer fills the open spaces depends on a combination of circumstances. First, sign extension is a property of the underlying hardware and beyond program control. sign-extend hardware copies the highest-order bit into the opened positions. So, the second circumstance is the value of the highest-order bit, highlighted with orange, when the shift operation occurs. So, the program produces the illustrated result only when the left operand is signed, the hardware performs sign extension, and the highest-order bit is 1. The left-hand operand and the result are shown in binary for clarity The right-hand operand, shown in decimal, is the number of places to shift the left operand The low-order bits on the right are discarded The vacated or opened high-order bits on the left are filled with a copy of the original left-most bit (in orange)

## Bitwise Operators With Assignment

Earlier in the chapter, we saw that C++ allows a shorthand notation with arithmetic operators called "Operation With Assignment." We can also use this notation with the binary bitwise operators.