C语言位操作符详解(有实用例题版)

目录

学前须知

按位与&

按位或|

按位异或^

按位取反~


学前须知

在C语言中四种位操作符它们分别是:&   |    ^   ~  

有一个小细节需要注意:它们的操作数必须是整数。

在介绍位操作符之前,我们要先知道:数字在计算机中计算使用补码,而我们看到的数字都是原码

负整数的二进制表示形式:有3种

原码直接根据数值写出的二进制序列就是原码

反码原码符号位不变,其他位按位取反就是反码

补码反码加1,就是原码

而正整数的原码补码反码都相同。

按位与&

&的巧计:只有对应的二进制位都为1时,结果位才为1下面给出一个例子:

#include<stdio.h>
int main()
{
	int a = -5;
	int b = 13;
	int c = a & b;
	//10000000000000000000000000000101 -5的原码
	//11111111111111111111111111111010 反码
	//11111111111111111111111111111011 补码
	//00000000000000000000000000001101 13的补码
	//00000000000000000000000000001001 9
	printf("%d\n", c);
	return 0;
}

先求出-5的原码再转换为按位取反变成反码,然后再加1变成补码,最后再进行按位与

代码结果如下:

例题:

有了上面的知识我们就可以解决“求一个数的二进制数中有多少个1”这个问题

于是我们就想到了用2求余来计算有多少个1

#include<stdio.h>
int main()
{
	int n = 0;
    scanf("%d", &n);
	int count = 0;
	while (n)
	{
		if (n % 2 == 1)
		{
			count++;
		}
		n = n / 2;
	}
    printf("%d\n",count);
	return 0;
}

当我们输入n=13时发现答案正如我们所料打印出3。

但是当我们输入-1时,答案竟然是0;

这显然在我们的意料之外,因为我们知道-1的补码有32个1。所以以上代码很明显对于复数来说不适用,这不满足我们的使用要求。

这时候我们就需要使用&操作符,再利用位移操作符>>n的二进制位移动i位,每次移动一位都用&1判断是否为1,为1就count++,这样就能求出有多少个1了!

#include<stdio.h>
int main()
{
	int n = -1;
	int count = 0; //计数器

	int i = 0;
	for (i = 0; i < 32; i++)
	{
		if (((n >> i) & 1) == 1)
			count++;
	}
	printf("%d\n", count);
	return 0;
}

但是我们还有更巧妙的方法:用n = n&(n-1)

如图可知每次n=n&(n-1)后就会有一个最右边的1被删去,这样一直循环下去就可以将所有的1都清楚掉,while循环在n等于0时就停止,这时只需要加上一个count计数器就可以算出有多少个1,代码如下:

#include<stdio.h>
int main()
{
	int n = 0;
	scanf("%d", &n);
	int count = 0;
	while (n)
	{
		n = n & (n - 1);
		count++;
	}
	printf("%d\n", count);
	return 0;
}

调试结果:

按位或|

I使用巧计:有1则为1,两个都是0才为0。下面给出一个例子方便理解:

#include<stdio.h>
int main()
{
	int a = -5;
	int b = 13;
	int c = a | b;
	//10000000000000000000000000000101 -5的原码
	//11111111111111111111111111111010 反码
	//11111111111111111111111111111011 补码
	//00000000000000000000000000001101 13的补码
	//11111111111111111111111111111111  -1
	printf("%d\n", c);
	return 0;
}

先求出-5的原码再转换为按位取反变成反码,然后再加1变成补码,最后再进行按位或

例题:

按位或可以将指定位置的二进制位改成1。下面例子是将13的二进制序列中的第5位改成1

#include<stdio.h>
int main()
{
	int a = 13;
	printf("改前a=%d\n", a);
	a = a | (1 << 4);
	//00000000000000000000000000001101 13的补码
	//00000000000000000000000000011101 29
	printf("改后a=%d\n", a);
	return 0;
}

代码运行结果如下:

按位异或^

按位异或的使用技巧是:相同为0,相异为1。举个例子方便理解:

#include<stdio.h>
int main()
{
	int a = -5;
	int b = 13;
	int c = a ^ b;
	//10000000000000000000000000000101 -5的原码
	//11111111111111111111111111111010 反码
	//11111111111111111111111111111011 补码
	//00000000000000000000000000001101 13的补码
	//11111111111111111111111111110110 结果的补码
	//11111111111111111111111111110101 结果的反码
	//00000000000000000000000000001010 结果的原码
	printf("%d\n", c);
	return 0;
}

要注意的是计算出来的结果的符号位是1,就说明是负数,所以需要依次求反码原码才能知道最终的结果,切忌直接将补码直接当成结果。

通过多次计算发现下面规律:a^b^a=b       a^a^b=b

 

例题:

这时有一道非常火的笔试题:不创建临时变量的情况下,交换两个数的值

有的同学上来一看题目,这不是简单吗?于是就写下如下代码

#include<stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	printf("交换前:a=%d,b=%d\n", a, b);
	int c = 0;
	c = a;
	a = b;
	b = c;
	printf("交换前:a=%d,b=%d\n", a, b);
	return 0;
}

运行结果也没问题,于是就直接看下一题了。

但是,请你注意题目要求的是不创建临时变量,而c是一个临时变量,这显然不符合题意。那么有聪明的同学就想到了下面这种方法:

#include<stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	printf("交换前:a=%d,b=%d\n", a, b);
	a = a + b;
	b = a - b;//本质上是(a+b)-b
	a = a - b;//本质上是(a+b)-a
	printf("交换前:a=%d,b=%d\n", a, b);
	return 0;
}

结果对了,于是就沾沾自喜。

但是他并没有考虑到溢出的问题,假如a+b恰好超过int能接受的范围呢?这时就出现了溢出,所以以上代码还是不够完美,那么怎么样才能够十分完美呢?这时我们就想到了按位异或操作符^

#include<stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	printf("交换前:a=%d,b=%d\n", a, b);
	a = a ^ b;
	b = a ^ b;//本质上是(a^b)^b
	a = a ^ b;//本质上是(a^b)^a
	printf("交换前:a=%d,b=%d\n", a, b);
	return 0;
}

运行结果如下:

按位取反~

按位取反的规则是这4个位操作符中最容易记的,就像他的名字一样:0变成1,1变成 0

 举个例子方便理解:

#include<stdio.h>
int main()
{
	int a = 0;
	//00000000000000000000000000000000
	//11111111111111111111111111111111 取反后
	//11111111111111111111111111111110 反码
	//10000000000000000000000000000001 原码
	printf("%d\n", ~a);
	return 0;
}

例题:

按位取反操作符配合按位与操作符可以产生一种妙用:可以将指定二进制位改成0

代码如下:

#include <stdio.h>
int main()
{
	int a = 13;
	a = a & ~(1 << 3);
	//00000000000000000000000000001101 13的补码
	//00000000000000000000000000000101 5
	printf("a = %d\n", a);
	return 0;
}

运行结果如下:

小结

本次分享到此结束,如果您觉得我写的还不错或者对您有帮助,请留下您宝贵的点赞,如果您发现我写的有什么错误,欢迎评论区留言!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值