1、第9章 位运算,2,第9章 位运算,本章要点: 六种位运算的基本概念、主要应用 位运算的混合使用,3,第9章 位运算,91 位运算的类型,92 位运算举例,93 位段,94 总结与提高,4,91 位运算的类型,C语言一共提供了六种位运算符:&:按位“与”|:按位“或”:按位“异或”:取反:右移这六种运算符号的优先级别(由高到低): & |。这六种运算符号的结合性为:为自右至左,其它为自左至右。,5,91 位运算的类型,要特别注意的是,参与运算的数是以补码方式出现的,并且参与运算的量应为整型或字符型,不能为实型数据。,6,91 位运算的类型按位“与”运算,按位“与”运算符“&”是双目运算符,其功
2、能是对参与运算的两数从低位到高位对应的位相与,“与”操作规定为:1&1的结果为1,0&1、1&0、0&0的结果都为0。,7,91 位运算的类型按位“与”运算,例9-1两个整数的“与”操作。 main() int a=5, b=6;int c=-5, d=-6;int x, y;x=a 该程序运行的结果为: 4,-6,8,91 位运算的类型按位“与”运算,说明:5的二进制数是00000101,6的二进制数是00000110,那么a&b的结果为4,如下式所示。00000101 (5) (&) 00000110 (6)00000100 (4) 而-5的二进制数补码是11111011,-6的二进制数补
3、码是11111010,因此c&d的结果为-6,如下式所示。11111011 (-5) (&) 11111010 (-6)11111010 (-6),9,91 位运算的类型按位“与”运算,按位“与”运算常用于位清零。欲将一个变量的某些位清零,只要同另一个相应位为其余位为的数进行“与”运算即可。同理,要提取某个数的一些位,只要将其同另一个相应位为而其余位为的数进行“与”运算即可。,10,91 位运算的类型按位“或”,按位“或”运算符“|”是双目运算符,其功能是参与运算的两数从低位到高位对应的位相或,“或”操作规定为:0|0的结果为0,1|1、1|0及0|1的结果都为1。,11,91 位运算的类型按
4、位“或”,按位“或”运算常用来对数据的某些位置1。假设int 型变量a的值为20000,它对应的16位二进制数是0100111000100000,表达式a|255的结果是变量a对应数值的低8位全置为1,高8位保留原样。如下式所示。0100111000100000 (20000) ( | ) 0000000011111111 (255) 0100111011111111 (20223)从上例中可以看出,如果要使某个数的一些位变成1,可以让这些位按位与1进行“或”运算,其它位则分别与0相“或”,保持不变。,12,91 位运算的类型按位“异或”,按位“异或”运算符“”是双目运算符,其功能是参与运算的
5、两数从低位到高位对应位相“异或”,“异或”运算规定:10和01的结果为1,11和00的结果为0。八进制数22与八进制数13按位进行“异或”的算式如下所示。00010010 (022) 00001011 (013)00011001 (031)与1进行“异或”运算的位将得到与该位相反的值,即101,110。与0进行“异或”运算的位则保持不变,即000,011。因此,“异或”运算可以方便地将数据的某些位翻转,而其它位不变。,13,91 位运算的类型取反,取反运算符“”是单目运算符,其功能是对参与运算的数的各二进制位按位求反。例如:00000000101100101111111101001101,14
6、,91 位运算的类型左移,左移运算符“”是双目运算符,它的功能是把“ ”左边的运算数的各二进位整体左移若干位,“”右边的数则指定移动的位数,左移过程中丢弃移出存储单元的高位数据,低位补0。例如: 表达式a4的结果是a中数据的各二进位向左移动4位。如a=00000011(十进制3),左移4位后为00110000(十进制48)。可见在高位没有溢出1的情况下,左移n位相当于该数乘以n个2。但此结论只适用于该数左移时被溢出舍弃的高位中不包含1的情况。左移比乘法运算快得多,有些C编译程序自动将乘2的运算用左移一位来实现,将乘以n个2的幂运算处理为左移n位。,15,91 位运算的类型右移,右移运算符“”是
7、双目运算符。其功能是把“ ”左边的运算数的各二进位全部右移若干位,“”右边的数指定移动的位数。 例如:设 a=15,a2表示把00001111右移为00000011(十进制3)。 应该说明的是,对于有符号数,在右移时,符号位将随同移动。当为正数时, 高位补0;而为负数时,高位是补0或是补1 取决于编译系统的规定。Turbo C和很多系统规定为补1。与左移运算对应的,右移n位相当于连续除以n个2,而且在除2时,舍弃小数部分,作取整运算。 正数不断右移结果是0,负数不断右移结果是-1。,16,92 位运算举例,例9-2 取一个整数a从右端开始的47位。,要取出a的47位,可以使用按位与运算,只要将
8、a与0000000011110000(十进制为240)按位与即可,这样(a ,17,92 位运算举例,在运行时输入217,则输出为:217a=217,b=13,18,92 位运算举例,也可以这样考虑: (1)先将a右移4位,使得47位挪到04位,这可以通过a4做到; (2)设置一个低4位全为1,其余位全为0的数,可以用下面的方法做到:(04)&(04),19,92 位运算举例,例9-3循环移位。 步骤如下:,(1)将a的左端n位先放到中间变量b的低n位中:b=a(16-n) (2)将b的高16-n位置0:b&(0n) (3)将a左移n位,其右边n位自动补0:c=an (4)将b与c进行按位或运
9、算:c=c|b,20,92 位运算举例,程序如下: main() unsigned a, b, c, dint n;scanf(“%u, %u“, ,21,92 位运算举例,其运行结果为: 38420,4 a=38420,n=4,d=24905,22,93 位段(位域),有时信息的存储不需一个完整的字节,而只需一个或几个二进制位。 格式,struct 位域结构名 位域列表 ; 其中位域列表的形式为:类型说明符 位域名:位域长度; 例如: struct bsint a:8;int b:2;int c:6;data;,23,93 位段,说明data为bs变量,共占两个字节。其中位域a占8位,位域b
10、占2位,位域c占6位。对data中的位域赋值可以是这样:data.a=2;data.b=1;注意,如果有表达式data.b=8,实际data.b的结果为0,因为8的二进制为1000,占4位,而b只占2位,则将二进制“1000”低两位“00”赋给了b,所以data.b=0。,24,93 位段,对于位域的定义有以下几点说明:,(1)一个位域必须存储在同一个存储单元中,不能跨两个存储单元,位域的长度不能大于存储单元的长度(存储单元可能是1个字节,可能是2个字节,不同的编译系统有所不同)。如一个存储单元所剩空间不足以存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。 stru
11、ct bs unsigned a:4unsigned :0 /*空域*/unsigned b:4 /*从下一单元开始存放*/unsigned c:4 ,25,93 位段,(2)位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:struct kint a:1int :2 /*无名位域,该2位不能使用*/int b:3int c:2;,26,93位段,(3)可以用输出整型数据的方式来输出位段的值,如:printf(“%d,%d,%d”,data.a,data.b,data,c); (4)位段在数值表达式中引用时将被系统自动转换成整数类型,如:data.a+5是合法的。,
12、27,94 总结与提高,位运算是C语言的一种特殊的运算,它是以二进制为单位进行运算,可以与赋值运算符号组合成复合运算符号。利用位运算可以完成汇编语言的某些功能,如位清零,移位等,使用位域可以在高级语言中实现数据压缩,节省了存储空间,提高了程序的效率。使用位操作可以减少除法和求余运算,理论上可以用位运算来完成所有的运算和操作,因而,灵活的位操作可以提高程序的效率。,28,94 总结与提高,例如:int a,ba=16/2b=16%2相当于int a,ba=161b=16-(1611)第2种写法更有技巧性,运行效率更高,不过这种写法要注意左移右移时最高位和最低位的变化。,29,94 总结与提高,另外,在嵌入式系统编程中,常用位操作对寄存器进行位设置,达到将其内容清零或屏蔽部分位的作用。,30,下课啦。,休息下。,