位掩码(bit mask),说白了是应用位运算来实现一些复杂的操作,利用二进制的1和0来实现。
例题1:给定了 32 位整数 N 和 M,如何将 N 的 i 到 j 位(位位置是 1,2,3,…32)设 置 为 和 M 中 相 同 位 的 值 。
例 如 , N = 00000000000000000000000001111011,
M = 00000000001000000011000000011000, i = 10, j = 20;那么,结果应该是 00000000001000000011000001111011
分析: 我们首先根据题意重现需要做的操作:( 1)我们需要从 M 中 get 第 i 到第 j 个位( 2)我们需要 clear N 中第 i 到第 j
个位( 3)我们需要 set N 中第 i 到第 j 个位。
对于( 1),根据基本操作, get bit 需要&1,所以与 M 进行操作的位掩码在第 i 到第 j 位应当为 1,其他位为 0。对于( 2),根据基本操作, clear bit 需要&0,所以与 N 进行操作的位掩码在第 i 到第j 位应当为 0,其他位为 1。注意,这个位掩码刚好是前一项操作位掩码的位反运算。对于( 3),我们只需要将( 1),( 2)的操作结果进行位或即可。构造所需位掩码的过程如前所述,对~0 进行基本操作和加减法即可。
参考解答:
#define INT_BIT_LENGTH (32)
void setBits(unsigned int &N, unsigned int M, int i, int j) {
unsigned int max = ~0;
unsigned int bitMask = (max << (INT_BIT_LENGTH - i) |
max >> j); //11..100..011..1
N = (M & (~bitMask)) | (N & bitMask);
}
例题2:将一个整数中相邻的奇数位和偶数位交换(位位置是 1,2,3,…32)。
分析: 同样的,我们首先根据题意重现我们需要做的操作:
( 1)我们需要 get 奇数位,根据基本操作易得需要与形如 1010…10的位掩码做位与。( 2) get 偶数位,根据基本操作易得需要与形如0101…01 的位掩码做位与。( 3) swap 意味着将( 1)中得到的结果右移一位,与( 2)中得到的结果左移一位,然后做位或操作。由此可见, 对于需要进行比特操作的题目,对题目要求进行分步,然后选择合适的位掩码,最后与给定二进制数进行基本位操作都是解题的关键。
参考解答:
int swapBits(int input) {
return ((0xaaaaaaaa & input) >> 1) | ((0x55555555 & input) << 1);
}
应用实例:
比如在管理系统中需要添加一个邮件管理,这个管理可以让用户决定几种类型的邮件接收与否。
每个用户可以在自己的后台进行设置,比如现在有4种类型的邮件emailTypeA, emailTypeB, emailTypeC, emailTypeD .
我在后台用户表添加了4个字段,属性true或false,这个每个用户可以设置修改自己的邮件接收与否。
做完后,用户那边的后台提出个问题,如果邮件类型在多一种或者几种的话,那必然要去修改数据库,后台代码和前端代码,如果使用位掩码来实现,则是一种比较好的思路。
刚才说的4种属性,那么我们就可以用4位的二进制就可以实现了,每一位都可以为0或者1,0为false,1为true,true和false决定接受与否,如果多加一个或多个属性,无非前面的二进制的一堆0的某一位换个1而已。
这样只要在数据库里添加一个字段 emialType,类型为integer
主要是怎么利用位运算进行操作了
1.获取,怎么利用获取出来的整数来确定各个属性
比如取出来的值为12,二进制位1100,也就是4个属性分别为 false,false,true,true。问题是怎么用代码(位运算)
那我们来看第一个属性的值 = 1100 位与 0001 值为 0000 与0001比较为false
第二个属性的值 = 1100 位与 0010 的值 0000 与0010比较为false
第三个属性的值 = 1100 位与 0100 的值 0100 与0100比较为true
第四个属性的值 = 1100 位与 1000 的值 1000 与1000比较为true
也就是第i个属性的值为
(emaiType & (1 << i) == ( 1 << i))
2. 设置 , 使用位掩码的这几个属性也就是有2个值0 和 1,true和false。
我们要设置这些属性的话就是把这个值的二进制的对应位取反
比如设置第二个属性,原值十进制为11, 二进制位 1011,也就是改成1001
用位运算怎么实现呢,这个实现想了很久,还找到网上又对这个实现进行封装了方法的, 后来发现位运算的异或其实可以直接实现某一位的取反操作。
原值11 二进制 1011
第二个属性取反 也就是 用1011 与 1(1 <<1)(0001) 异或就可以了, 异或的规则是同0异1
1011
^ 0001
== 1010 刚好取反
再比如第三位取反
1011
^ 0100 (1 <<2 )
== 1111
我们来分析这个异或的实现,实现某一位的取反就是1 移位到这一位,也就是二进制这一位为1,其他位位0
开始分析,其他位: 用0去异或,如果原值为1,不同为1,原值不变,如果原值为0,相同为0,原值不变,也就是其他位保持不变。
要修改的位: 用1去异或,如果原值为1,相同为0 , 原值改变 ,如果原值为0, 不同为1,原值改变,可见用异或可以实现指定位的取反。
第i个属性的修改为:
emaiType ^ ( 1 << i)
默认值的设置,这里是4个属性,设置了15,可是如果以后属性添加的话又得需要改了,高人提供的方法,设置 -1, -1的二进制全是1☺你们懂得
至此,问题全部搞定,具体使用读者还需细细体会。