最近在刷位运算的题目,经常用的位运算还比较熟悉,但对于左移、右移运算符和其应用实践有待学习。这里统一总结一下C++的位运算。
学习整理自此链接处
1.总表
| 运算符 | 名称 | 作用 |
|---|---|---|
| & | 按位与 | 两个操作数同时为1,结果为1 |
| | | 按位或 | 两个操作数只要有一个为1,结果为1 |
| ~ | 按位非 | 按位取反,操作数为1,结果为0;操作数为0,结果为1 |
| ^ | 按位异或 | 两个操作数相同,结果为0;两个操作数不同,结果为1 |
| << | 左移 | 右侧空位补0,左移一位相当于将原数扩大两倍 |
| >> | 右移 | 左侧空位补符号位,右移一位相当于将原数缩小两倍 |
注:以上操作符都是位运算符,是对位进行操作的。如果我们用十进制3&5,则电脑在处理时会将十进制数转换成机器语言(二进制),并且二进制数在内存中一般是8位位一个字节进行存储的。所以结果应该是3和7的二进制按位与后的结果
2.按位与
| 按位与运算示例 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
|---|---|---|---|---|---|---|---|---|
| 3 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
| 7 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
| 结果返回十进制数3 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
同样,按位或、异或、非,等都可以通过这种方式进行计算。
按位与用途
1.清零
若想对一个存储单元清零,即使其全部二进制位为0,只要找一个二进制数,其中各个位符合一下条件:原来的数中为1的位,新数中相应位为0。然后使二者进行&运算,即可达到清零目的。
最简单的符合以上条件的数是0,即任何数和0相与都是0。
2.取一个数中某些指定位
最简单的示例是,与1按位相与,取出最后一位的数字;与3按位相与,取出最后两位的数字。
3.按位或
| 按位或运算示例 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
|---|---|---|---|---|---|---|---|---|
| 3 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
| 7 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
| 结果返回十进制数7 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
按位或用途
按位或运算常用来对一个数据的某些位定值为1。
例如:如果想使一个数a的低3位改为1,则只需要将a与7进行按位或运算即可,如示例。
4.按位异或
按位或运算的两个二进制位值相同则为0,否则为1。
| 按位异或运算示例 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
|---|---|---|---|---|---|---|---|---|
| 3 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
| 7 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
| 结果返回十进制数4 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
按位异或用途
根据异或运算规则有
1.可以通过特定位上的1使原位上的数反转。1^ 0=1,1^1=0
2.可以通过特定位上的0使原位上的数得到保留。0^ 0=0,0^ 1=1。
3.第三种用途之前在此文章总结过,即不使用额外的空间来交换两值。
a=a^b;
b=b^a;
a=a^b;
举例说明:
| 按位异或运算示例 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
|---|---|---|---|---|---|---|---|---|
| a=3 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
| b=7 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | 1 |
| 结果返回十进制数a=a^b=4 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
| 结果返回十进制数b=b^a=3 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
| 结果返回十进制数b=a^b=7 | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
最终实现了两值的交换,是不是特别有趣!其中,a^ b 和 b^ a等同。
5.按位取反
它是一元运算符,用于求整数的二进制反码,即分别将操作数各二进制位上的1变为0,0变为1。
计算机具体的运算过程和原码、反码、补码的相关知识可以查看此处链接文章
| 按位取反运算示例 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
|---|---|---|---|---|---|---|---|---|
| 3的源码 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
| 3的反码 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
| 3的补码 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
| 取反 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
| 负数的补码 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
| 负数的反码等于补码减1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 | 1 |
| 负数-4的源码 | 1 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
| 按位取反运算示例 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
|---|---|---|---|---|---|---|---|---|
| -3的二进制源码 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 1 |
| -3的反码 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 0 |
| -3的补码 | 1 | 1 | 1 | 1 | 1 | 1 | 0 | 1 |
| 取反操作 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
| 取反后得到正数的补码 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
| 正数的反码等于反码 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
| 结果2的源码 | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
关键:1.在计算机系统中,数值一律用补码来表示(存储)
2.最高位是符号位,0表示是正数,如果是1的话就表示负数。
3.正数的反码=补码=原码
4.负数的补码等于反码加1,注意取反码时,符号位不变。
5.取反的计算机过程为:源码->反码->补码->取反操作->补码->反码->源码。
6.左移运算符
左移运算符是用来将一个数的各二进制位左移若干位,移动的位数由右操作数指定(右操作数必须是非负值),其右边空出的位用0填补,高位左移溢出则舍弃该高位。
| 左移运算示例 | 128 | 64 | 32 | 16 | 8 | 4 | 2 | 1 |
|---|---|---|---|---|---|---|---|---|
| 15 | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
| 15<<2==60 | 0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 |
从上面的码表中也可以看出,每左移一位则原数扩大两倍
用途:1<<x==pow(2,x)
7.右移运算符
右移运算符是用来将一个数的各二进制位右移若干位,移动的位数由右操作数指定(右操作数必须是非负值),移到右端的低位被舍弃,对于无符号数,高位补0。对于有符号数,某些机器将对左边空出的部分用符号位填补(即“算术移位”),而另一些机器则对左边空出的部分用0填补(即“逻辑移位”)。
例: a的值是八进制数113755,
a:1001011111101101 (用二进制形式表示)
a>>1: 0100101111110110 (逻辑右移时)
a>>1: 1100101111110110 (算术右移时)
8.位运算赋值运算符
位运算符与赋值运算符可以组成复合赋值运算符。
例如: &=, |=, >>=, <<=, ^=
例: a & = b相当于 a = a & b
a <<= 2 相当于 a = a << 2