C程序设计讲师:徐红波Email:x_h_b@tom.com第12章位运算第10章介绍的指针运算和本章将介绍的位运算很适合于编写系统软件的需要,是C语言的重要特色。在计算机用于检测和控制领域中要用到位运算的知识,因此应当学习和掌握本章的内容。所谓位运算是指进行二进制位的运算。在系统软件中,常要处理二进制位的问题。例如,将一个存储单元中的各二进制位左移或右移一位,两个数按位相加等。C语言提供位运算的功能,与其他高级语言相比,它显然具有很大的优越性。12.1位运算符和位运算C语言提供如下表所列出的位运算符说明:(1)位运算符中除~以外,均为二目(元)运算符,即要求两侧各有一个运算量。(2)运算量只能是整型或字符型的数据,不能为实型数据。运算符含义运算符含义&按位与~取反|按位或左移^按位异或右移12.1.1“按位与”运算符(&)参加运算的两个数据,按二进制位进行“与”运算。如果两个相应的二进制位都为1,则该位的结果值为1;否则为0。即0&0=00&1=01&0=01&1=1按位与有一些特殊的用途:(1)清零。如果想将一个单元清零,即使其全部二进制位为0,只要找一个二进制数,其中各个位符合以下条件:原来的数中为1的位,新数中相应位为0。然后使二者进行&运算,即可以达到清零目的。(2)取一个数中某些指定位。如有一个整数a(2个字节),想要其中的低字节。只需将a与(377)8按位与即可。(3)要想将哪一位保留下来,就与一个数进行&运算,此数在该位取1。12.1.2“按位或”运算符(|)两个相应的二进制位中只要有一个为1,该位的结果值为1。即0|0=00|1=11|0=11|1=1按位或运算常用来对一个数据的某些位定值为1。12.1.3“异或”运算符(^)异或运算符^也称XOR运算符。它的规则是:若参加运算的两个二进制位同号,则结果为0(假);异号则为1(真)。即0^0=00^1=11^0=11^1=0“异或”的意思是判断两个相应的位值是否为“异”,为“异”(值不同)就取值(1);否则为假(0)。说明^运算符的应用:(1)与1相^,使特定位翻转(2)与0相^,保留原值(3)交换两个值,不用临时变量a=a^b;b=b^a;a=a^b;12.1.4“取反”运算符(~)~是一个单目(元)运算符,用来对一个二进制数按位取反,即将0变1,将1变0。12.1.5左移运算符()用来将一个数的各二进制位全部左移若干位。高位左移后溢出,舍弃。左移1位相当于该数乘以2。左移比乘法运算快得多,有些C编译程序自动将乘2的运算用左移一位来实现,将称2n的幂运算处理为左移n位。12.1.6右移运算符用来将一个数的各二进制位全部右移若干位,移到右端的低位被舍弃。在右移时,需要注意符号位问题。对无符号数,右移时左边高位移入0;对于有符号的值,如果原来符号位为0(该数为正),则左边也是移入0。如果符号位原来为1(即负数),则左边移入0还是1,要取决于所用的计算机系统。有的系统移入0,有的系统移入1。移入0的称为“逻辑右移”,即简单右移;移入1的称为“算术右移”。12.1.7位运算赋值运算符位运算符与赋值运算符可以组成复合赋值运算符,例如:&=,|=,=,=,^=等。12.1.8不同长度的数据进行位运算如果两个数据长度不同(例如long型和int型)进行位运算时(如a&b,而a为long型,b为int型),系统会将二者按右端对齐。如果b为正数,则左侧16位补满0;若b为负数,左端应补满1;如果b为无符号整数型,则左端填满0。12.2位运算举例[例12.1]取一个整数a从右端开始的4~7位[例12.2]循环移位。要求将a进行右循环移位,将a右循环移n位,即将a中原来左面(16-n)位右移n位,原来右端n位移到最左面n位12.3位段以前曾介绍过对内存中信息的存取一般以字节为单位。实际上,有时存储一个信息不必用一个或多个字节,例如,“真”或“假”用0或1表示,只需1位即可。在计算机用于过程控制、参数检测或数据通信领域时,控制信息往往只占一个字节中的一个或几个二进制位,常常在一个字节中放几个信息。向一个字节中的一个或几个二进制位赋值和改变它的值,可以用以下两种方法:(1)可以人为地将一个整型变量data分为几部分。(2)位段结构体C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这样以位为单位的成员称为“位段”或称“位域”。利用位段能够用较少的位数存储数据。例如:structpacked_data{unsigneda:2;unsignedb:6;unsignedc:4;unsignedd:4;inti;}data;关于位段的定义和引用,有几点要说明:(1)位段成员的类型必须指定为unsigned或int类型。(2)某一位段可以从另一个存储单元开始存放。(3)一个位段必须存储在同一存储单元中,不能跨两个单元。如果第一个单元空间不能容纳下一个位段,则该空间不用,而从下一个单元起存放该位段。(4)可以定义无名字段。(5)位段的长度不能大于存储单元的长度,也不能定义位段数组。(6)位段可以用整型格式符输出。(7)位段可以在数值表达式中引用,它会被系统自动地转换成整型数。习题12.1编写一个函数getbits,从一个16位的单元中取出某几位(即该几位保留原值,其余位为0)。函数调用形式为getbits(value,n1,n2),value为该16位(2个字节)中的数据值,n1为欲取出的起始位,n2为欲取出的结束位,例如:getbits(0101675,5,8)表示对八进制101675这个数,取出它的从左面起第5位到第8位。12.4编写一函数用来实现左右循环移位。函数名为move,调用方法为move(value,n),其中value为要循环位移的数,n为位移的位数。例如,n0表示为左移;n0为右移。n=4表示要右移4位;n=-3表示要左移3位。12.1#includestdafx.h“unsignedshortgetbits(unsignedshortvalue,intn1,intn2){return(unsignedshort)(value(n1-1))(16-(n2-n1+1));}intmain(intargc,char*argv[]){printf(%x\n,getbits(0101675,5,8));return1;}12.4#includestdafx.hunsignedshortmove(unsignedshortvalue,intn){unsignedshorta,b;if(n0){a=value(-n);b=value(16-(-n));}elseif(n0){a=valuen;b=value(16-n);}returna|b;}intmain(intargc,char*argv[]){printf(%x\n,move(255,4));printf(%x\n,move(255,-4));return1;}