位操作的应用实例(2)位掩码

位掩码(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☺你们懂得

       至此,问题全部搞定,具体使用读者还需细细体会。
 

 

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值