【C++基础】系列博客为参考《C++ Primer中文版(第5版)》(C++11标准)一书,自己所做的读书笔记。
本文为原创文章,未经本人允许,禁止转载。转载请注明出处。
1.前言
位运算符作用于整数类型的运算对象,并把运算对象看成是二进制位的集合。
一般来说,如果运算对象是“小整型”,则它的值会被自动提升成较大的整数类型。运算对象可以是带符号的,也可以是无符号的。如果运算对象是带符号的且它的值为负,那么位运算符如何处理运算对象的“符号位”依赖于机器。而且,此时的左移操作可能会改变符号位的值,因此是一种未定义的行为。
⚠️关于符号位如何处理没有明确的规定,所以强烈建议仅将位运算符用于处理无符号类型。
2.移位运算符
⚠️移位运算符右侧的运算对象一定不能为负(例如将下方程序改为auto a = bits << -1
,编译就会报错),而且值必须严格小于结果的位数(例如将下方程序改为auto a = bits << 33
,编译就会报错),否则就会产生未定义的行为。
1
2
3
4
5
6
7
8
int main() {
unsigned char bits = 11;
auto a = bits << 1;//a被自动提升为int
auto b = bits >> 1;//b被自动提升为int
cout << int(bits) << endl;//输出为:11
cout << a << endl;//输出为:22
cout << b << endl;//输出为:5
}
分析上方程序,数值11的二进制为0000 1011
,左移一位为0001 0110
,所以a为22;右移一位为0000 0101
,所以b为5。
这里用的都是补码,正数的原码和补码是一样的。相关知识戳:【C++基础】第六课:类型转换。
左移运算符(<<
)在右侧插入值为0的二进制位。右移运算符(>>
)的行为则依赖于其左侧运算对象的类型:如果该运算对象是无符号类型,在左侧插入值为0的二进制位;如果该运算对象是带符号类型,在左侧插入符号位的副本或值为0的二进制位,如何选择要视具体环境而定。
3.位求反运算符
位求反运算符(~
)将运算对象逐位求反后生成一个新值,将1置为0、将0置为1:
char
类型的运算对象首先提升成int
类型,提升时运算对象原来的位保持不变,往高位添加0即可。因此在本例中,首先将bits
提升成int
类型,增加24个高位0,随后将提升后的值逐位求反。
4.位与、位或、位异或运算符
与(&
)、或(|
)、异或(^
)运算符在两个运算对象上逐位执行相应的逻辑操作:
⚠️有一种常见的错误是把位运算符和逻辑运算符搞混了,比如位与(&
)和逻辑与(&&
)、位或(|
)和逻辑或(||
)、位求反(~
)和逻辑非(!
)。
5.使用位运算符
假设班级中有30个学生,可以使用一个二进制位代表某个学生是否通过了测试(1为通过,0为不通过):
1
unsigned long quiz1=0;
unsigned long
在任何机器上都将至少拥有32位。使用一个数表示学生27通过了测试:
1
1UL<<27;//生成一个值,该值只有第27位为1
1UL
为1,二进制为0000 .... 0001
;1UL<<1
为2,二进制为0000 .... 0010
;1UL<<2
为4,二进制为0000 .... 0100
,剩余以此类推。
更新quiz1
的值:
1
quiz1 |= 1UL << 27;//表示学生27通过了测试
|=
运算符的工作原理和+=
非常相似,它等价于:
1
quiz1 = quiz1 | 1UL << 27;
类似的还有&=
、^=
。
最后,再次核查学生27的测试结果:
1
bool status = quiz1 & (1UL << 27);//学生27是否通过了测试?
我们将quiz1
和一个只有第27位是1的数值按位求与,如果quiz1
的第27位是1,计算的结果就是非0(真);否则结果是0。
6.移位运算符(又叫IO运算符)满足左结合律
因为移位运算符满足左结合律,所以表达式:
1
cout << "hi" << " there" << endl;
的执行过程实际上等同于:
1
((cout << "hi") << " there") << endl;
移位运算符的优先级不高不低,介于中间:比算数运算符的优先级低,但比关系运算符、赋值运算符和条件运算符的优先级高。因此在一次使用多个运算符时,有必要在适当的地方加上括号使其满足我们的要求:
1
2
3
cout << 42+10;//正确:+的优先级更高,因此输出求和结果
cout << (10 < 42);//正确:括号使运算对象按照我们的期望组合在一起,输出1
cout << 10 < 42;//错误:试图比较cout和42!
最后一个cout
的含义其实是:
1
( cout << 10) < 42;
也就是“把数字10写到cout
,然后将结果(即cout
与42进行比较)”。