简述位操作符
额,似乎这不应该由我来写,不过既然已经开始写了,那就把它完成好了~
大家都知道,数据在计算机中是以二进制的方式进行储存,而二进制只用0和1来表示数,如果对应成真值,则是True或False 。0和1往往给人一种简谐美的感觉,正如莱布尼茨所说,他从0和1中看到了宇宙的创生。额,似乎跑题了,拉回来~
而位操作符则是用来通过对存储的二进制数操作的一些符号。
PS:如非特殊声明,以下均是指整数而非浮点数
1.位操作符
常见的位操作符有如下:
类似+=等复合符号,除了~之外,均有=&、=|、^=复合符号。
而且,位操作符中存在“移动操作符”:<<和>>
exp<<n表示把exp中的位往左移动n位,移出边界的则被丢弃,从右边开始用0补空位。
exp>>n表示把exp中的位往右移动n位,移出边界的则被丢弃,从左边开始用符号位补空位。
PS:很多书上都写着>>也是从左边开始用0补空位,但是经过我的Lieo的实际测试,实际上是用符号位补充。
PPS:符号位将在后面介绍~
2.实例
春节到了,酒店也开始忙开了,很多人为了和家人一起吃年夜饭,都会去酒店预定房间。每个房间有两个状态:被预订和尚未被预订。
酒店经理KC每天都要审查客房的预订情况,做好分析。但是普通的记录方法会浪费很多时间和资源,于是他决定用一个整数和位操作来简化工作。
这里我们假设一个无符号的16位整数(unsigned short)n,每一位表示酒店的一个房间,如果房间被预订,那么这位上的数字就是1,否则是0。
开始时,所有的房间都是尚未被预订,所以状态均为0。相应的,我们只需要简单的初始化n即可:
后来,KC接到电话,3号客房被预定,于是,我们需要翻转第三位:
这样,第三位便从0被翻转成了1
陆续的,7号和13号客房也被人预订。类似的我们也要翻转第7位和第13位:
后来,由于特殊原因,7号房间的客人取消了预订,于是我们有需要把第7位翻转回去
由于位操作比较底层,容易出错,所以通常会把它弄成宏或者封装起来使用。在标准的C++库中,就有一个Bitset类。不过我们这次不在此讨论,有兴趣的可以自己去翻翻参考文档~
3.原码、反码和补码
我们在上面的例子中,使用的是无符号整数,最高位符号位被舍弃。下面我们就来看下有符号整数在内存中的二进制形式。
对于有符号整数来说,比如16位的有符号整数short,他的最高位被用来标示数字的符号,0位正数,1为负数,因此,存储位只有7位,所以他只能表示-32767~32768.
那么什么是原码呢?通常来说,正数的原码就是其本身,而负数的原码除符号位之外,其余位和正数原码相同。
反码则是把负数除了符号位之外的其他位,按位非处理,如果把负数反码加上1,则得到的是补码。而有符号整数通常都是以补码的形式进行保存。
CAUTION:正数的反码、补码都是其本身
举个例子来加深印象:
理解完原码、反码和补码之后,我们在好回头看看前面的位操作。
当我们使用>>移动时,左边空出来的都用符号位去填充,所以导致了-1>>n出来的都是-1的情况。而对一个有符号整数进行~操作时,也极易出现问题,因为符号位也被翻转了。
------------------------------------------EOF---------------------------------------
大家都知道,数据在计算机中是以二进制的方式进行储存,而二进制只用0和1来表示数,如果对应成真值,则是True或False 。0和1往往给人一种简谐美的感觉,正如莱布尼茨所说,他从0和1中看到了宇宙的创生。额,似乎跑题了,拉回来~
而位操作符则是用来通过对存储的二进制数操作的一些符号。
PS:如非特殊声明,以下均是指整数而非浮点数
1.位操作符
常见的位操作符有如下:
操作符 | 作用 | 用法 | 真值表 |
~ | 按位非 | ~exp | ~0 -> 1 ~1->0 |
& | 按位与 | exp1 & exp2 | exp均为1才为1 |
| | 按位或 | exp1 | exp2 | exp中有一个为1则为1 |
^ | 按位异或 | exp1 ^ exp2 | 1^0才为1 |
类似+=等复合符号,除了~之外,均有=&、=|、^=复合符号。
而且,位操作符中存在“移动操作符”:<<和>>
exp<<n表示把exp中的位往左移动n位,移出边界的则被丢弃,从右边开始用0补空位。
exp>>n表示把exp中的位往右移动n位,移出边界的则被丢弃,从左边开始用符号位补空位。
PS:很多书上都写着>>也是从左边开始用0补空位,但是经过我的Lieo的实际测试,实际上是用符号位补充。
PPS:符号位将在后面介绍~
2.实例
春节到了,酒店也开始忙开了,很多人为了和家人一起吃年夜饭,都会去酒店预定房间。每个房间有两个状态:被预订和尚未被预订。
酒店经理KC每天都要审查客房的预订情况,做好分析。但是普通的记录方法会浪费很多时间和资源,于是他决定用一个整数和位操作来简化工作。
这里我们假设一个无符号的16位整数(unsigned short)n,每一位表示酒店的一个房间,如果房间被预订,那么这位上的数字就是1,否则是0。
开始时,所有的房间都是尚未被预订,所以状态均为0。相应的,我们只需要简单的初始化n即可:
引用:
unsigned short n = 0;
引用:
short a = 0;
a |= (1 << 2);
// 1: 0000 0000 0000 0001
// 1 << 2: 0000 0000 0000 0100
陆续的,7号和13号客房也被人预订。类似的我们也要翻转第7位和第13位:
引用:
short a = 0;
a |= (1 << 2);
a |= (1 << 6);
a |= (1 << 12);
引用:
short a = 0;
a |= (1 << 2);
a |= (1 << 6);
a |= (1 << 12);
a ^= (1 << 6);
3.原码、反码和补码
我们在上面的例子中,使用的是无符号整数,最高位符号位被舍弃。下面我们就来看下有符号整数在内存中的二进制形式。
对于有符号整数来说,比如16位的有符号整数short,他的最高位被用来标示数字的符号,0位正数,1为负数,因此,存储位只有7位,所以他只能表示-32767~32768.
那么什么是原码呢?通常来说,正数的原码就是其本身,而负数的原码除符号位之外,其余位和正数原码相同。
反码则是把负数除了符号位之外的其他位,按位非处理,如果把负数反码加上1,则得到的是补码。而有符号整数通常都是以补码的形式进行保存。
CAUTION:正数的反码、补码都是其本身
举个例子来加深印象:
有符号整数 | 原码 | 反码 | 补码 |
56 | 0000 0000 0011 1000 | 0000 0000 0011 1000 | 0000 0000 0011 1000 |
-56 | 1000 0000 0011 1000 | 1111 1111 1100 0111 | 1111 1111 1100 1000 |
理解完原码、反码和补码之后,我们在好回头看看前面的位操作。
当我们使用>>移动时,左边空出来的都用符号位去填充,所以导致了-1>>n出来的都是-1的情况。而对一个有符号整数进行~操作时,也极易出现问题,因为符号位也被翻转了。
------------------------------------------EOF---------------------------------------