C语言笔记:位运算问题

位运算简介

C语言中的各种运算都是以字节的形式进行,在编写很多系统程序时,如驱动程序、磁盘文件管理程序等,常要求将数据按位(bit)进行运算或者处理。计算机中的数据(不管是整型,浮点型,还是字符型)在内存中都是以二进制的形式进行存储的,位运算就是直接对内存中的二进制数进行操作,位运算比一般算数运算要快,如果要考虑开发高效率的程序,位运算是必不可少的

常见的位运算符(运算符优先级按表下到上依次增高,如~为最高优先级)

运算符含义功能
~按位取反对一个二进制数按位取反,0变为1,1变为0
<<左移不管是有符号数还会无符号数,左移时右侧补0
>>右移对于无符号数,右移时左侧补0;对于有符号数,对于有符号数,右移时左侧补符号位(如果是正数补0,负数就补1,也叫算数移位)
&按位与两个相应二进制位都为1,则相应结果二进制位为1,否则为0
^按位异或两个相应二进制位相异,则相应结果二进制位为1;如相同,则为0
|按位或两个相应二进制位都为0,则相应结果二进制位为0,否则为1

位运算实例

1、给定一个整数a,设置a的二进制表示中bit5为1,其它位不变

#include <stdio.h>
int main(void)
{
 //十进制形式 
 int d=195;//C语言中不能直接使用二进制,可以用十进制或者十六进制、八进制赋值 
 d|=1<<5;
 printf("d=%#d\n",d);//#为输出相应进制前缀 
 //八进制形式
 int o=0303; //八进制必须以0开头
 o|=1<<5;
 printf("o=%#o\n",o); 
 //十六进制形式
 int h=0XC3;
 h|=1<<5;
 printf("h=%#X\n",h);   
 return 0;
}

输出:
在这里插入图片描述
代码释疑:
这里的变量d,o,h在数值上完全相等,只是以不同的数制进行表示,它们在内存中的二进制形式,都为1100 0011,设置bit5为1后都为1110 0011。
1<<5完全可以用0b100000(0b或0B是二进制数前缀)代替,两者等价。

按位或的用法:
经常被用于实现将一个数的某些位设置成1。将mask(上述代码为100000)中指定位设置成1,其它位设置成0,则s|=mask。使得s的指定位置设置为1。

在这里插入图片描述

1-2、给定一个整数a,设置a的bit5~bit10为1,其它位不变

#include <stdio.h>
int main(void)
{
 //十六进制形式
 int a=0XC3;//0000 0000 1100 0011
 a|=0x3F<<5;//十六进制数0x3F对应二进制0000 0000 0011 1111,左移5位后为0000 0111 1110 0000 
 printf("a=%#X\n",a);//最终二进制结果为0000 0111 1110 0011,对应十六进制数0X7E3  
 return 0;
} 

输出:
在这里插入图片描述
在这里插入图片描述

2、给定一个整形数a,它的bit5清除0,其它位保持不变

#include <stdio.h>
int main(void)
{
 int a=0xff30;//对应二进制数为1111 1111 0011 0000
 a&=~(1<<15);//对应a=a&0111 1111 1111 1111
 printf("a=%#x\n",a);//最终a=0111 1111 0011 0000(bit15清零了,注意,下标从bit0开始) 
 return 0;
} 

输出:
在这里插入图片描述
代码释疑:
二进制0111 1111 0011 0000对应十六进制0x7f30。

font color=red>按位与的用法:
经常被用于将特定位清0,将mask中指定位设置为0,s&=mask,实现了s指定位清0
在这里插入图片描述
由以上四个例子得知,在位运算中:置1用“位或1”;清0用“位与0”

2-2、给定一个整数a,设置a的bit5~bit10为0,其它位不变

#include <stdio.h>
int main(void)
{
 int a=0xff30;//对应二进制数为1111 1111 0011 0000
 a&=~(0X3F<<5);//构造一个bit5~bit10为0的二进制数为,1111 1000 0001 1111 
 printf("a=%#x\n",a);//最终a=1111 1000 0001 0000(bit5~bit10清零了,注意,下标从bit0开始) 
 return 0; 
} 

输出:
在这里插入图片描述
在这里插入图片描述

3、给定一个整数a,求a得bit3~bit8对应的整数

问题分析:

  • 首先构造一个bit3~bit8都为1,其余位数都为0的二进制数,然后将这个数与a进行“位与”操作
  • 接着再将操作结果进行右移3位,就可得到bit0~bit5为原来a的bit3~bit8的值
#include <stdio.h>
int main(void)
{
 int a=0xFBA;//a对应二进制为0000 1111 1011 1010
 a&=0x3F<<3;//0x3F对应的二进制为0000 0000 0011 1111,左移三位后为0000 0001 1111 1000
 printf("测试输出:a=%#X\n",a);//测试输出0000 0001 1011 1000对应的十六进制0X1B8
 a>>=3;//要输出其对应整数,还要右移回3位 
 printf("a的bit3~bit8对应整数:%#X\n",a);//输出a对应的二进制为0000 0000 0011 0111的十六进制数0X37
 return 0;
}

输出:
在这里插入图片描述
在这里插入图片描述

4、用C语言给一个寄存器(32位)的bit7~bit17赋值937(其余位不受影响)

问题分析:
嵌入式程序中应用比较多,操作寄存器离不开位运算。寄存器的特点是按位进行规划和使用,但是寄存器的读写确实整体32位一起进行的,也就是说只想修改bit7~bit17是不行的,必须整体32bit全部写入,寄存器操作要求就是,在设定特定位时不能影响其它位。所以,当想改变一个寄存器中某些特定位时,不会直接去写寄存器,而是先从寄存器中把整体32位二进制数读取出来,然后在此基础上修改特定的值,最后再将修改后的32位二进制数整体写入寄存器。

  • 第一步:将bit7~bit17清0,不能影响其他值
  • 第二步:将937写入bit7~bit17
#include <stdio.h>
int main(void)
{
 int a=0XACF71;//0000 0000 0000 1010 1100 1111 0111 0001
 a&=~(0X7FF<<7);//0X7FF对应二进制0111 1111 1111,左移7位再取反,即可构造一个bit7~bit17为0,其余位为1的二进制数 
       //再进行位与运算,即可将a的bit7~bit17清0
 a|=937<<7;//实现将a的bit7~bit17赋值937整数,其余位不变
 printf("a=%#X\n",a);  
 return 0;
}

输出:
在这里插入图片描述

在这里插入图片描述

4-2、用C语言给一个寄存器的bit7~bit17赋值937,同时给bit21~bit25赋值17

int a=0XACF71;
a&=~((0X7FF)<<7|(0X1F<<7));//将a的bit7~bit17以及bit21~bit25清0
a|=(937<<7)|(17<<21);//0X229D4F1

代码释疑:
a=0XACF71,对应二进制数0000 0000 1010 1100 1111 0111 0001,绿色部分分别对应bit21~bit25,bit7~bit17,分别替换成17(1 0001),937(011 1010 1001),最后a对应的二进制数为10 0010 1001 1101 0100 1111 0001,对应十六进制为0X229D4F1

5、用C语言实现将一个寄存器的bit7~bit17的值加17(其余位不受影响)

具体步骤如下:

  • 第一步:读出寄存器的bit7~bit17的值
  • 第二步:将第一步读出的值加上17
  • 第三步:将寄存器的bit7~bit17清0
  • 第四步:将第二步算的值写入bit7~bit17
#include <stdio.h>
int main(void)
{
 int a=0XACF71;
 unsigned tmp;//用于存放寄存器bit7~bit17原来的值以及增加17后的值
 tmp=a&(0X7FF<<7);//读出a的bit7~bit17值,并存放到tmp变量
 tmp>>=7;//获取a的bit7~bit17的值
 tmp+=17;//将获取的值加上17 
 a&=~(0X7FF<<7);//将a的bit7~bit17值清0,其余位不变
 a|=tmp<<7;//将a的bit7~bit17赋值tmp整数,其余位不变
 printf("a=%#X\n",a); 
 return 0;
} 

输出:
在这里插入图片描述
代码释疑:
int a=0XACF71对应的二进制位为1010 1100 1111 0111 0001,绿色部分为bit7~bit17,这部分数再加上17(1 0001)变为10 1101 0111 1,a的bit7~bit17整体清零在赋值tmp,所以最后a对应的二进制数为1010 1101 0111 1111 0001,对应十六进制数为0XAD7F1

由上述代码得出按位与(&)的用法:

  • 保留特定位,将mask的特定位设置为1,其它位设置为0,则s&=mask,实现了s的特定位被保留
 tmp=a&(0X7FF<<7);//读出a的bit7~bit17值,并存放到tmp变量
  • 特定位清0,将mask的特定位设置成0,其它位设置成1,则s&=mask,实现了s的特定位清0
 a&=~(0X7FF<<7);//将a的bit7~bit17值清0,其余位不变

6、使用位运算判断一个整数是奇数还是偶数

判断一个整数是奇数还是偶数,可以用对2求余的思想判断,如果这是一个小整数还可以,如果是一个大整数,那么求余思想就会很低,我们还有一个特有方法:根据这个整数对应的二进制形式的末位为0表示偶数,为1表示奇数的特点,即能判断它是奇数还是偶数。

#include <stdio.h>
int main(void)
{
 int n;
 printf("请输入一个整数:\n");
 scanf("%d",&n);
 if((n&1)==1)
 {
  printf("%d是奇数\n",n);
 }
 else
 {
  printf("%d是偶数\n",n);
 }
 return 0;
}
奇数偶数
1(00012(0010
3(00114 (0100
5(01016(0110

7、使用位运算计算一个int整数的二进制数中有多少个1

#include <stdio.h>
int CountOne(int a);
int main(void)
{
 int a;
 printf("输入一个整数:");
 scanf("%d",&a);
 printf("%d的二进制表示中1的个数是%d\n",a,CountOne(a));
 return 0;
} 
int CountOne(int a)
{
 int c=0;//记录二进制表示1的个数
 while(a)
 {
  c++;
  a&=a-1;
 } 
 return c;
}

输出:
在这里插入图片描述
在这里插入图片描述

8、使用位运算的“异或”运算符完成两个变量值的交换

按位异或(^)两个相应二进制位相异,则相应结果二进制位为1;如相同,则为0,因为“异或”具有交换律,可完成两个变量值的交换。其中异或有两个规律:

规律一规律二
两个相同的数据进行“异或”运算结果为0一个数与0进行“异或”运算结果仍为原数
#include <stdio.h>
int main(void)
{
 int a,b;
 a=7,b=5;//初始a=7,b=5 
 a=a^b;
 b=b^a;//此时完成b的值变为a的值
 a=a^b;//此时完成a的值变为b的值
 printf("a=%d b=%d\n",a,b);
 return 0;
}

输出:
在这里插入图片描述
代码释疑:

  • 代码第6行、第7行:
a=a^b;
b=b^a;//此时完成b的值变为a的值

相当:
b=b^a=b^a^b=b^b^a=a

红色部分利用规律一,红色与绿色部分用到规律二。

  • 代码第8行:
    同理可完成a的值变为b的值
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值