理论教育 位运算符:C语言中的6种常用操作方法

位运算符:C语言中的6种常用操作方法

时间:2023-10-19 理论教育 版权反馈
【摘要】:C语言提供了位运算的功能,它是以二进制位为单位进行运算的。标准C语言提供了6种位运算符。例如,两个字符型数30&9的运算过程如下。读者会发现PORTD只有VT3亮了,说明PORTD的值是0b00001000,这是因为Data1的二进制形式为00011110,Data2的二进制形式为00001001,两者按位运算的结果为00001000。

位运算符:C语言中的6种常用操作方法

单片机系统中,对于开关量的控制和信号量的读取而言,最佳的操作就是位操作。C语言提供了位运算的功能,它是以二进制位为单位进行运算的。位运算符只有逻辑运算和移位运算两类,这使得C语言也能像汇编语言一样用来编写高效的位操作程序。标准C语言提供了6种位运算符(见表4-6)。

表4-6 C语言位运算符表

978-7-111-33274-9-Chapter04-61.jpg

1.按位与运算

按位与运算符“&”是双目运算符。其功能是参与运算的两数从右到左各对应的二进位进行与操作。只有对应的两个二进位均为1时,结果位才为1,否则为0。参与运算的数以其内存存储形式(即补码形式)为准。例如,两个字符型数30&9的运算过程如下。

00011110 (30的二进制形式,由于是字符型,所以用8位二进制表示)

& 00001001 (9的二进制形式,由于是字符型,所以用8位二进制表示)

-----------------

00001000 (8的二进制形式,字符型数据按位与的结果还是字符型)

下面通过实例的运行效果来理解按位与运算符的使用方法。

【例4-7】编程实现把字符型变量Data1和Data2初始化为0x1E和9,然后对Data1和Data2进行按位与运算并把结果赋值给Data1,然后把Data1送D口输出。所用硬件电路图为图4-1。

设计过程

1)修改S041项目中的main.c的内容,改为如下所示。

978-7-111-33274-9-Chapter04-62.jpg

2)将以上代码编译后用Proteus VSM运行一次,查看PORTD的值。读者会发现PORTD只有VT3亮了,说明PORTD的值是0b00001000,这是因为Data1的二进制形式为00011110,Data2的二进制形式为00001001,两者按位运算的结果为00001000。

按位与运算在实际应用中通常用来对变量的某一位清零(bitClear)或者用来屏蔽某些无关位。

●清零的例子:要把某特殊寄存器Data的最低位置为1但还不想影响Data其他位的值。读者可以使用“Data=Data&0b11111110;”来实现。这样按位与之后,Data的其他7位都不会变化,只有最低位被变成0,达到了把最低位清零并且不影响其他位的目的。

●屏蔽无关位的例子:要读取D口低4位的值作乘3运算并把结果送到字符变量Data中,为了防止读取数据时D口高4位的干扰,读者可以用“Data=3*(PORTD&0x0F);”来获得正确结果,这样无论PORTD此时高4位为何值都不会干扰运算结果。这里0x0F术语被称为掩码。

2.按位或运算

按位或运算符“|”是双目运算符。其功能是参与运算的两数从右到左各对应的二进位进行或操作。只有对应的两个二进位有一个为1时,结果位才为1,否则为0。参与运算的数以其内存存储形式(即补码形式)为准。例如,两个字符型数30&-3的运算过程如下。

00011110 (30的二进制形式,由于是字符型,所以用8位二进制表示)

|11111101 (-3的二进制形式,由于-3是负数,所以内存存储的是其补码)

------------------

11111111 (255的二进制形式)

下面通过实例的运行效果来学习按位或运算符的使用方法。

【例4-8】编程实现把字符型变量Data1和Data2初始化为30和-3,然后对Data1和Data2进行按位或运算并把结果赋值给Data1,然后把Data1送D口输出。所用硬件电路图为图4-1。

设计过程

1)修改S041项目中的main.c的内容,改为如下所示。

978-7-111-33274-9-Chapter04-63.jpg

2)将以上代码编译后用Proteus VSM运行一次,查看PORTD的值。读者会发现PORTD控制的LED全亮了,这说明PORTD的值是0b11111111,原理请读者自行分析。

这里有一点需要说明的是,对于负数而言,在进行位运算前要先通过计算得到其内存存储结果(相应的补码)后方能进行位运算。不能直接运算,否则得到的结果是错误的。

3.按位取反运算

取反运算符“~”是单目运算符,具有右结合性。其功能是对参与运算的数的各对应的二进位进行取反操作。例如,~9(设9是字符型数据)的运算如下。

978-7-111-33274-9-Chapter04-64.jpg

此运算符的实验例子请读者自行设计并验证。

4.按位异或运算

按位异或运算符“^”是双目运算符。其功能是参与运算的两数从右到左各对应的二进位进行异或或操作。当两个对应的二进位不同时,结果为1,否则为0。参与运算的数以其内存存储形式(即补码形式)为准。例如,两个字符型数30&-3的运算过程如下。

00011110 (30的二进制形式,由于是字符型,所以用8位二进制表示)

^ 11111101 (-3的二进制形式,由于-3是负数,所以内存存储的是其补码)

------------------(www.daowen.com)

11100011 (227的二进制形式)

【例4-9】编程实现把字符型变量Data1和Data2初始化为30和-3,然后对Data1和Data2进行按位异或运算并把结果赋值给Data1,然后把Data1送D口输出。所用硬件电路图为图4-1。

设计过程

1)修改S041项目中的main.c的内容,改为如下所示。

978-7-111-33274-9-Chapter04-65.jpg

2)将以上代码编译后用Proteus VSM运行一次,查看PORTD的值。读者会发现PORTD控制的VT2、VT3、VT4灭,其他的亮了,这说明PORTD的值是0b11100011,原理请读者自行分析。

5.左移运算

左移运算符“<<”是双目运算符。其一般形式为a<<b,其功能为把a对应的二进位全部左移b位,在左移过程中,a的最高位左移后就丢弃了,低位自动补0。例如:

978-7-111-33274-9-Chapter04-66.jpg

表示把Data对应的二进位0b00000011(十进制数3)向左移动1位,左移1位后为00000110(十进制数6)。细心的读者会发现左移一位相当于算数运算中的×2运算。同理左移两位相当于算数运算中的×4运算。但是注意这种等价关系在左移时只有要左移的最高位不是1时才有效。例如,当Data=0b10000001(对应无符号十进制数是129)时,左移1位后结果是0b00000010(对应无符号十进制数是2,左侧最高位的1在左移时超出了字符型的存储范围被CPU自动丢弃了),此时就不是×2运算了。

此运算符的实验例子请读者自行设计并验证。

6.右移运算

右移运算符“>>”是双目运算符。其一般形式为a>>b,其功能为把a对应的二进位全部右移b位,在右移过程中,a的最低位右移后就丢弃了,最高位自动补0(正数)或1(负数)。例如:

978-7-111-33274-9-Chapter04-67.jpg

表示把0b000001000(十进制数8)右移为0b00000100(十进制数4)。读者会发现当右移过程移出的位没有1时,右移1位相当于算数运算的除以2,右移两位相当于除以4。

应该说明的是,对于有符号数,在右移时,符号位将随同移动。当正数右移时,最高位补0,而负数右移时,符号位为1,最高位是补0或是补1取决于所用编译系统的规定。PICC中规定为补1。

此运算符的实验例子请读者自行设计并验证。

7.位运算的使用例子:压缩BCD码解码

压缩BCD码是在某些通信系统中使用的一种数据存储格式。BCD码英文全称为Binary-CodedDecimal‎,是一种用二进制编码的十进制代码。这种编码形式利用了4个二进制位来储存一个十进制的数码,使二进制和十进制之间的转换得以快捷地进行。非压缩BCD码用一个字节表示一位十进制数,高4位总是0000,低4位的0000~1001表示0~9。例如,0b00001000表示十进制数8。压缩BCD码中分别用一个字节的高低4位各表示一个十进制数。这样一个字节就能表示两位十进制数。例如,0b10010110表示十进制数96。

【例4-10】在某通信系统中,通信用的数字是以一个字节的压缩BCD码存储的,用EnCode表示。请编程将其解码为一个十进制普通数字。例如,传来的数据是0x89(十六进制),则其对应的十进制数应该是89,对应十六进制数应该是0x59。所用硬件电路图为图4-1。

设计过程

1)修改S041项目中的main.c的内容,改为如下所示。

978-7-111-33274-9-Chapter04-68.jpg

2)将以上代码编译后用Proteus VSM运行一次,查看PORTD的值。读者会发现PORTD的值是0b1011001,对应的十六进制就是0x59,转换成十进制就是89了。原理请读者阅读本例代码中的注释。

8.C语言的置位操作和清零操作

在单片机控制程序中,经常需要对特殊寄存器的某一个二进制位置位或清零,还不能影响此寄存器的其他位。利用位运算可做到这一点。

1)置位(bitset)。又称为置1,也就是把变量的某二进制位变为1,而其他位保持不变,可以使用按位或运算来实现。例如:

978-7-111-33274-9-Chapter04-69.jpg

这样不论chTmp原先是多少,和0b01000000作按位或运算以后,总能使第6位(见下段的阅读约定)为1,而其他位不变。

阅读约定:本书中在对位操作提到第几位时,总是从第0位开始查的。例如,一个字节,共8位,从低到高依次是第0位,第1位,……,第6位,第7位。这里的第0位就是日常生活口语中的第1位。

2)清零(bitclear)。又称为清0,也就是把变量的某一个二进制位变为0,而其他位保持不变,可以使用按位与运算来实现。例如:

978-7-111-33274-9-Chapter04-70.jpg

这样chTmp和0b10111111进行按位与运算后,总能使第6位为0,其他位保持不变。

【例4-11】编程使字节变量chTmp第4位为1,第0位为0,其他位不变。

解答

978-7-111-33274-9-Chapter04-71.jpg

语句分析

这里的小括号很重要,当读者在一个表达式中使用多个运算符时,为了防止记错运算符优先级(C语言的运算符有15级,一般人很难记准),建议用小括号明确标示出先算谁,后算谁。这样能减少程序出错的可能。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈