目录

学前须知
在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;
}
运行结果如下:

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

1575

被折叠的 条评论
为什么被折叠?



