Team LiB
Previous Section Next Section

5.3. The Bitwise Operators

5.3. 位操作符

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.

位操作符(表5-3)使用整型的操作数。位操作符将其整型操作数视为二进制位的集合,为每一位提供检验和设置的功能。另外,这类操作符还可用于 bitset 类型(第 3.5 节)的操作数,该类型具有这里所描述的整型操作数的行为。

Table 5.3. Bitwise Operators
表 5.3. 位操作符

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.

对于位操作符,由于系统不能确保如何处理其操作数的符号位,所以强烈建议使用unsigned整型操作数。

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 类型有 8 位。位求反操作符(~)的功能类似于 bitsetflip 操作(第 3.5.2 节):将操作数的每一个二进制位取反:将 1 设置为 0、0 设置为 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.

左移操作符(<<)在右边插入 0 以补充空位。对于右移操作符(>>),如果其操作数是无符号数,则从左边开始插入 0;如果操作数是有符号数,则插入符号位的副本或者 0 值,如何选择需依据具体的实现而定。移位操作的右操作数不可以是负数,而且必须是严格小于左操作数位数的值。否则,操作的效果未定义。

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.

位与操作(&)需要两个整型操作数,在每个位的位置,如果两个操作数对应的位都为 1,则操作结果中该位为 1,否则为 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(||).

常犯的错误是把位与操作(&)和逻辑与操作(&&)(第 5.2节)混淆了。同样地,位或操作(|)和逻辑或操作(||)也很容易搞混。



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

下面我们用图解的方法说明两个 unsigned char类型值的位与操作,这两个操作数均用八进制字面常量初始化:

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.

位异或(互斥或,exclusive or)操作符(^)也需要两个整型操作数。在每个位的位置,如果两个操作数对应的位只有一个(不是两个)为 1,则操作结果中该位为 1,否则为 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.

位或(包含或,inclusive or)操作符(|)需要两个整型操作数。在每个位的位置,如果两个操作数对应的位有一个或者两个都为 1,则操作结果中该位为 1,否则为 0。

result = b1 | b2;

5.3.1. Using bitset Objects or Integral Values

5.3.1. bitset 对象或整型值的使用

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 类比整型值上的低级位操作更容易使用。观察下面简单的例子,了解如何使用 bitset 类型或者位操作来解决问题。假设某老师带了一个班,班中有 30 个学生,每个星期在班上做一次测验,只有及格和不及格两种测验成绩,对每个学生用一个二进制位来记录一次测试及格或不及格,以方便我们跟踪每次测验的结果,这样就可以用一个bitset对象或整数值来代表一次测验:

     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.

使用 bitset 类型时,可根据所需要的大小明确地定义 bitset_quiz1,它的每一个位都默认设置为 0 值。如果使用内置类型来存放测验成绩,则应将变量 int_quiz1 定义为 unsigned long 类型,这种数据类型在所有机器上都至少拥有32位的长度。最后,显式地初始化 int_quiz1 以保证该变量在使用前具有明确定义的值。

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:

老师可以设置和检查每个位。例如,假设第27位所表示的学生及格了,则可以使用下面的语句适当地设置对应的位:

     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:

如果使用 bitset 实现,可直接传递要置位的位给 set 函数。而用 unsigned long 实现时,实现的方法则比较复杂。设置指定位的方法是:将测验数据与一个整数做位或操作,该整数只有一个指定的位为 1。也就是说,我们需要一个只有第 27 位为 1 其他位都为0的无符号长整数(unsigned long),这样的整数可用左移操作符和整型常量 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:

然后让这个整数与 int_quiz1 做位或操作,操作后,除了第 27 位外其他所有位的值都保持不变,而第 27 位则被设置为 1。这里,使用复合赋值操作(第 1.4.1 节)将位或操作的结果赋给 int_quiz1,该操作符 |= 操作的方法与 += 相同。于是,上述功能等效于下面更详细的形式:

     //  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:

如果老师重新复核测验成绩,发现第 27 个学生实际上在该次测验中不及格,这时老师应把第 27 位设置为 0:

     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.

使用 bitset 的版本可直接实现该功能,只要复位(reset)指定的位即可。而对于另一种情况,则需通过反转左移操作后的结果来实现设置:此时,我们需要一个只有第 27 位为 0 而其他位都为 1 的整数。然后将这个整数与测验数据做位与操作,把指定的位设置为 0。位求反操作使得除了第 27 位外其他位都设置为 1,然后此值和 int_quiz1 做位与操作,保证了除第 27 位外所有的位都保持不变。

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

最后,可通过以下代码获知第 27 个学生是否及格:

     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.

使用 bitset 的版本中,可直接读取其值判断他是否及格。使用 unsigned long 时,首先要把一个整数的第 27 位设置为 1,然后用该整数和 int_quiz1 做位与操作,如果 int_quiz1 的第 27 位为 1,则结果为非零值,否则,结果为零。

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.

一般而言,标准库提供的 bitset 操作更直接、更容易阅读和书写、正确使用的可能性更高。而且,bitset 对象的大小不受 unsigned 数的位数限制。通常来说,bitset 优于整型数据的低级直接位操作。



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.

重写 bitset 表达式:使用下标操作符对测验结果进行置位(置 1)和复位(置 0)。

5.3.2. Using the Shift Operators for IO

5.3.2. 将移位操作符用于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.

输入输出标准库(IO library)分别重载了位操作符 >><< 用于输入和输出。即使很多程序员从未直接使用过位操作符,但是相当多的程序都大量用到这些操作符在IO标准库中的重载版本。重载的操作符与该操作符的内置类型版本有相同的优先级和结合性。因此,即使程序员从不使用这些操作符的内置含义来实现移位操作,但是还是应该先了解这些操作符的优先级和结合性。

The IO Operators Are Left Associative
IO 操作符为左结合

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.

在这个语句中,操作数"hi"与第一个 << 符号结合,其计算结果与第二个 << 符号结合,第二个 << 符号操作后,其结果再与第三个 << 符号结合。

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:

移位操作符具有中等优先级:其优先级比算术操作符低,但比关系操作符、赋值操作符和条件操作符优先级高。若 IO 表达式的操作数包含了比IO操作符优先级低的操作符,相关的优先级别将影响书写该表达式的方式。通常需使用圆括号强制先实现右结合:

     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语句解释为:

     (cout << 10) < 42;

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

该表达式说“将 10 写到 cout,然后用此操作(也就是 cout)的结果与 42 做比较”。

Team LiB
Previous Section Next Section