极致高效!C语言位运算讲解及必备技巧:快速统计二进制 1。(图文并茂,深度解析)

一.前言

       C语言里有各种各样的操作符,尤其是移位操作符(<<、>>)和位运算符(&、|、^)在底层编程、性能优化等场景下特别实用,玩得溜的话能写出更高效的代码。这篇文章会带你用这些操作符实现几个小技巧:快速数出内存中一个整数的二进制里有多少个1、比较两个数的二进制差异、以及判断一个数是不是2的幂次方。这些技巧看着简单,但在实际开发中能帮大忙。

二.位运算基础回顾

2.1 移位操作符

2.1.1 左移操作符(<<)

       在C语言中,左移操作符(<<)是一种位操作符,用于将操作数的所有位向左移动指定的位数。移位规则是:左边抛弃,右边补零

注意:无论num在这里是正数还是负数,左移相当于给该数乘以2。因此,num<<1的结果是20。 

 2.1.2 右移操作符(>>)

           在C语言中,右移操作符(>>)是一种位操作符,用于将操作数的所有位向右移动指定的位数。右移可以分为两种,其一是逻辑右移、其二是算术右移

逻辑右移:左边用零填充,右边丢弃!

算术右移:左边用该值的符号位填充,右边丢弃!

逻辑右移演示

算术右移演示 

     

注意:大多数编译器在运行右移操作符的时候都是算术右移!!!

注意:对于右移操作符来说,如果操作数是一个正整数,相当于给该数除以2。负数不满足!!!

int num = 10;
num = num >>1;

num的结果就是5!

int num = 5;
num = num >> 1;

num的结果就是2,相当于num/2。

2.2 位操作符

 2.2.1 按位与(&)

       在C语言中,按位与操作符 &对两个操作数的每一位进行逻辑与运算。只有当两个对应位都为 1 时,结果位才为 1,否则为 0。

注意:位操作都是对补码进行操作!!!

// 0000 0011 (3的补码)

// 1000 1001 (-9的补码)

// 0000 0001  (结果为:1的补码,正数的补码与原码一致,结果为1。)

2.2.2 按位或(|)

        在C语言中,按位或操作符 | 对两个操作数的每一位进行逻辑或运算。只要两个对应位中有一个为 1,结果位就为 1,否则为 0。

注意:位操作都是对补码进行操作!!!

// 0000 0011 (3的补码)

// 1000 1001 (-9的补码)

// 1000 1011  (结果为:-11的补码,需要先取反再+1,变成原码为1111 0101,结果为-117)

三.内存中存储二进制数1的个数

3.1方法一

int main()
{
	unsigned int n = 0;
	int count = 0;
	printf("请输入要求的数:>");
	scanf("%d", &n);
	while (n)
	{
		if (n % 2 == 1)
		{
			count++;
		}
		n = n / 2;
	}
	printf("内存中二进制1的个数为:%d", count);
	return 0;
}

①对于一个二进制数,先将其%2可以可以判断最后一位是0还是1,再将其/2可以去除最后一位。这个过程类似于一个十进制数,现将其%10可以判断最后一位是0~9中哪一个,再将其/10可以去除最后一位。然后依次循环这个过程即可。

②另一个需要注意点是n被定义为unsigned int无符号整数,这是因为内存中整数是以补码形式存放的,为了消除负数在%2和/2过程中的影响,无论输入的数是正还是负都按无符号整数读入。

3.2 方法二

int main()
{
	int n = 0;
	int count = 0;
	printf("请输入要求的数:>");
	scanf("%d", &n);
	for (int i = 0;i < 32;i++)
	{
		if ((n >> i) & 1)
		{
			count++;
		}
	}
	printf("内存中二进制1的个数为:%d", count);
	return 0;
}

①遍历循环int类型的32个二进制位,每次右移1位,并将右移的结果与1按位与。如果是1,说明最右边的1位也是1;如果是0,说明最右边的1位也是0。 

比如 int n = 10

// 00000000 00000000 00000000 00001010 (10的补码)

// 00000000 00000000 00000000 00000001 (1的补码)

// 00000000 00000000 00000000 00000000 (结果为0,说明10的二进制表示中最右边1位是0)

3.3 方法三(推荐

int main()
{
	int n = 0;
	int count = 0;
	printf("请输入要求的数:>");
	scanf("%d", &n);
	while (n)
	{
		n &= (n - 1);
		count++;
	}
	printf("内存中二进制1的个数为:%d", count);
	return 0;
}

①这个方法的关键在于n &= (n-1),这个算法每执行一次可以去除n的二进制中的一个1。

四.判断两个数的二进制数有几位不同

       对于两个整数,其在内存中以二进制补码形式存放。如果想要判断有几位不同,可以考虑先异或,相同为0,不同为1。之后再统计这个结果中1的个数。

int main()
{
    int a, b;
    printf("请输入两个数:>");
    scanf("%d %d", &a, &b);
    int c = a ^ b;
    int count = 0;
    while (c)
    {
        count++;
        c &= (c - 1);
    }
    printf("不同位有:%d位", count);
   return 0;
}

五.快速判断一个数是否为2的幂次方

int main()
{
    int n = 0;
    printf("请输入这个数:>");
    scanf("%d", &n);
    if (n & (n - 1))
    {
        printf("%d不是2的幂次方", n);
    }
    else
    {
        printf("%d是2的幂次方", n);
    }
}

①这里同样用的n&(n-1),2的幂次方数的特点是其存放在内存中的二进制数只有一个1。

因此,n&(n-1)这个操作只执行一次,对于2的幂次方数来说就可以使其变为0。如果没有变为0,那就意味着二进制数中还有其它1,那必然不是2的幂次方数!!!


通过本文的学习,我们系统地掌握了 C 语言中移位操作符(<<>>)与按位运算符(&|^~)的几大经典应用:快速统计整数二进制中 “1” 的个数、比较两个数的二进制差异、以及瞬间判断一个数是否为 2 的幂。位运算虽然看似“小技巧”,却能在算法性能和代码简洁度上带来实实在在的提升,也能帮助你更深刻地理解计算机底层的数据表示与运算机制。

如果你在实践中遇到其他有趣的位运算场景,或者对文中方法有不同的思考和改进,欢迎在评论区分享你的心得与疑问。让我们一起交流、讨论,共同进步!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值