整形变量中位为1的个数

最近在做关于关于Socket通信相关的框架封装,想要遍历同一局域网中所有的设备,通过手机扫描获得设备列表,然后建立Socket通信。

遍历局域网中的设备无非就是发一个UDP广播(组播),然后等待接收确认消息。但是在参考别人的代码的时候,发现也有通过给同一局域网内所有主机一对一发送UDP消息来实现的,这种实现的有点在于有些网络设置广播和组播都不可用,在此种情况下就能比较完整地搜索出设备;缺点是一对一发送数据太多,时间长,且每一个都需要等待一段时间的返回,若是网络差一点返回的消息就慢,普通的192.168这种局域网都需要好几分钟才能搜索完成。

所以,我在封装SDK框架的时候,把这两种方法都实现了一遍。广播和组播是很容易的,只要获取广播或者组播地址,剩下的就是一对一单点通信了。当实现遍历同一局域网中的主机的时候,需要获取同一局域网所以的主机IP,一般的做法是默认这一局域网是192.168开头的C类地址,这样的话只需要遍历254个IP,对着254个IP分别发消息确认是否是设备。但为了让框架能够兼容A类和B类地址,自己加入了更多的判断。

因为IP地址可能会被分成子网或者超网,不论是哪种,都是通过子网掩码来确定主机号和网络号的,所以可以通过子网掩码来获取主机号的位数,然后就可以拼凑出所有主机的IP地址。

获取同一局域网内的所有IP算法伪代码如下:

//获取本机ip地址
int localIP = ...
//获取本机子网掩码
int localMask = ...
//根据子网掩码计算IP地址中的主机号的位数
int hostBitCount = ...
//同一局域网主机号全为0的IP地址
int baseIP = localIP & localMask;
//主机号位数为hostBitCount,那么就大约有2^hostBitCount个IP地址
for(int i = 0; i < 2^hostBitCount; ++i){
    availableIP = baseIP | (主机号为i并且网络号为0的IP地址);
}

子网掩码是网络号为1主机号为0的IP地址,计算hostBitCount就是计算子网掩码中有多少个0。最能想到的方法就是:

for (int i = 0; i < 32; i++) {
    if((localMask & 1) == 0){
        hostBitCount++;
    }
    localMask = localMask >> 1;
}

上述代码就是通过移位32次来判断0的个数,但是因为之前接触过一些位运算的简洁算法,就百度出了另外的一种复杂度更低的算法。因为目前只是考虑IPv4地址,所有IP地址的总位数是32固定的,那么只要知道有多少个1,也就知道了有多少个0。

首先考虑一个类似的问题,如何最简单的判断一个数是不是2的幂,2的幂的特点是二进制表示中只有一位是1,其他的都是0。那么这就可以通过位运算来判断:

//2的幂例:
x       =   000...1...000
x - 1   =   000...01..111
x & (x - 1) = 0

//非2的幂例:
x       =   010..1...000
x - 1   =   010..01..111
x & (x - 1) != 0

当一个数x是2的幂的时候,那么x & (x -1)这个表达式的值必定是0,因为x只有一位为1,那么x - 1就会把这个1借位减,x - 1则会这个1就会变成0,比1小的位数的0变成1,相当于取反操作,进行&操作就全部变成了0。而如果不是2的幂的话,就不会不止一位为1,那么即使一个1和比这个1低的全部去反,还有另外的1不会改变,那么x-1就不会变成0。

根据上面的计算方法可以进一步思考,若是令 x = x & (x - 1),那么每执行一次这个表达式,就相当于去掉二进制中的一个1,因为1那个位和后面的位都通过&操作置成0了。那么想要得到1的个数,只需要一直计算到x == 0,其中计算的次数就是1的个数,那么上述获得主机号的位数就能能简单的实现了:

//主机号位数
int hostBitCount = 32;
//计算int中0的位数
while(localMask != 0){
    localMask &= localMask -1 ;
    hostBitCount--;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值