在C语言中,每当提起位操作符时可能对于一部分老铁来说可能会有点懵逼,因为位操作符是用来操作二进制序列的,二进制会比较麻烦。但是如果我们去思考研究二进制,那么问题就会迎刃而解,接下来我就分享一些关于位操作符的一些小技巧吧。
看一个例题,写一个函数,返回参数15二进制序列中1的个数:
1.第一种解法
我们知道整形数据是以二进制补码存在计算机中的,而正数的原反补码都是相同的,所以15在计算机中的存储形式就是00000000000000000000000000001111。如何得出1的个数呢?对15和1进行按位与运算。相同的位即为1,不相同的位即为0。1的二进制序列为00000000000000000000000000000001。正好能得出15二进制序列中最后一位是0还是1。然后再向右移一位,再次进行运算。
int main()
{
int a = 15;
int i = 0;
int count = 0;//计数器
for (i = 0; i < 32; i++)//重复32次
{
int b = a & 1;
if (1 == b)
count++;
a = a >> 1;
}
printf("%d\n", count);
return 0;
}
//提高效率的写法
int main()
{
int a = 15;
int i = 0;
int count = 0;//计数器
while(a)//重复到没有二进制序列中没有1为止
{
int b = a & 1;
if (1 == b)
count++;
a = a >> 1;
}
printf("%d\n", count);
return 0;
}
2.第二种解法
可以不使用位操作符就可以实现按位与和右移的作用。
模2可以起到第一种方法中的与1取按位与的作用;除2则可以起到右移一位的作用。可以与十进制中数的模10和除10进行类比,是相同的。
int main()
{
int a = 15;
int count = 0;//计数器
while(a)
{
int b = a % 2;
if (1 == b)
count++;
a = a / 2;
}
printf("%d\n", count);
return 0;
}
但是本方法有个弊端,就是在处理负数的时候无法得到正确的数值。当a为-1时,得到的数值为0。而-1是以补码的形式储存到计算机中的。
-1的原码:10000000000000000000000000000001
-1的反码:11111111111111111111111111111110
-1的补码:11111111111111111111111111111111
因此打印时应该是32的1才对。究其原因还是因为/ 和 %没有在二进制序列上做文章。仅仅只是对数据进行的处理。-1 % 2得到的是-1,-1 /2 = 0,而不是1,所以打印出0。所以这个算法只适合正数。
附:有一个解决方法,就是在初始化a时,初始化为unsigned int型的数据。因为这样-1就不再是负数了,它的补码的首位号1也就不会被理解为负数的标志了。所以能正确地输出答案。
3.第三种解法
接下来要介绍的是最巧妙的一个算法,话不多说先上算法:
int main()
{
int a = 15;
int i = 0;
int count = 0;//计数器
while (a)
{
a = a & (a - 1);
count++;
}
printf("%d\n", count);
return 0;
}
这串代码和之前代码最大的不同就是a = a&(a-1),并且也没有了判断条件,计数器直接加一:
每执行一次计数器就加一。这个算法要多多思考,才能理解。
例:判断一个数是不是2的k次方。
int main()
{
int a = 0;
scanf("%d", &a);
if (0 == (a & (a - 1)))
printf("该数是2的k次方");
else
printf("该数不是2的k次方");
return 0;
}
只有当a为2的k次方时,才只有它的k+1位为1,其他均为零。因此减一后后面的位全部变成1。
以上就是我要分享的一些小技巧了,如果您觉得还不错,麻烦您给个赞!