位运算卢佩新目录按位与、或、异或、取反运算移位运算掩码异或运算的一些特性引言整数在计算机中用二进制的位来表示C语言提供一些运算符可以直接操作整数中的位,称为位运算,这些运算符的操作数都必须是整型的有些信息利用整数中的某几个位来存储,要访问这些位,仅仅有对整数的操作是不够的,必须借助位运算目录按位与、或、异或、取反运算移位运算掩码异或运算的一些特性按位与、或、异或、取反运算按位与(BitwiseAND)运算符&按位或(BitwiseOR)运算符|按位取反(BitwiseNOT)运算符~按位异或(BitwiseXOR)运算符^C语言中其实并不存在8位整数的位运算操作数在做位运算之前都至少被提升为int型例:(IntegerPromotion)unsignedcharc=0xfc;//c=?unsignedinti=~c;//i=?目录按位与、或、异或、取反运算移位运算掩码异或运算的一些特性移位运算移位运算符(BitwiseShift)左移左移将一个整数的各二进制位全部左移若干位例如0xcfffffff32得到0x3fffffcc:移动的位数必须小于左操作数的总位数如上例子,如果左移的位数大于等于32位,结果?在一定的取值范围内,将一个整数左移1位相当于乘以2对有符号数和无符号数都成立,对负数也成立如果左移改变了最高位(符号位),则不成立移位运算符(BitwiseShift)右移当操作数是无符号数时,右移运算的规则和左移类似例如0xcfffffff32得到0x33fffffc:和左移类似,移动的位数也必须小于左操作数的总位数,否则结果是Undefined。在一定的取值范围内,将一个整数右移1位相当于除以2,小数部分截掉。移位运算符(BitwiseShift)右移当操作数是有符号数时,右移运算的规则比较复杂:如果是正数,那么高位移入0如果是负数,那么高位移入1还是0不一定,这是Implementation-defined的。对于x86平台的gcc编译器,最高位移入1,也就是仍保持负数的符号位,这种处理方式对负数仍然保持了“右移1位相当于除以2”的性质。建议只对无符号数做位运算,以减少出错的可能。练习一下面两行printf打印的结果有何不同?inti=0xcffffff3;printf(%x\n,0xcffffff32);printf(%x\n,i2);目录按位与、或、异或、取反运算移位运算掩码异或运算的一些特性掩码如果要对一个整数中的某些位进行操作,可以用掩码(Mask)来表示掩码操作在进行CPU相关寄存器操作时非常有用经常需要对CPU的某一个引脚进行拉低拉高操作例:掩码0x0000ff00对一个32位整数的8~15位进行操作取出8~15位unsignedinta,b,mask=0x0000ff00;a=0x12345678;b=(a&mask)8;/*0x00000056*///b=(a8)&~(~0U8);将8~15位清0unsignedinta,b,mask=0x0000ff00;a=0x12345678;b=a&~mask;/*0x12340078*/将8~15位置1unsignedinta,b,mask=0x0000ff00;a=0x12345678;b=a|mask;/*0x1234ff78*/练习一统计一个无符号整数的二进制表示中1的个数函数原型是intcountbit(unsignedintx);练习二用位操作实现无符号整数的乘法运算函数原型是unsignedintmultiply(unsignedintx,unsignedinty);例如:(11011)2×(10010)2=((11011)21)+((11011)24)。练习三对一个32位无符号整数做循环右移函数原型是unsignedintrotate_right(unsignedintx,intn);所谓循环右移就是把低位移出去的部分再补到高位上去例如rotate_right(0xdeadbeef,8)的值应该是0xefdeadbe。目录按位与、或、异或、取反运算移位运算掩码异或运算的一些特性异或运算的一些特性一个数和自己做异或的结果是0如果需要一个常数0,x86平台的编译器可能会生成这样的指令:xorl%eax,%eax该指令比同样效果的movl$0,%eax指令快异或的真值表左值右值值000011101110不管是0还是1,和0做异或保持原值不变和1做异或得到原值的相反值位翻转unsignedinta,b,mask=1U6;a=0x12345678;b=a^mask;/*flipthe6thbit*/。如果a1^a2^a3^...^an的结果是1,则表示a1、a2、a3...an之中1的个数为奇数个,否则为偶数个。奇偶校验(ParityCheck)在串口通信过程中,每个字节的数据都计算一个校验位,数据和校验位一起发送出去,这样接收方可以根据校验位粗略地判断接收到的数据是否有误x^x^y==y,因为:x^x==00^y==y交换两个变量的值,不得借助额外的存储空间利用位运算a=a^b;b=b^a;a=a^b;练习一请在网上查找有关RAID(RedundantArrayofIndependentDisks,独立磁盘冗余阵列)的资料,理解其实现原理练习二交换两个变量的值,不得借助额外的存储空间,除了本节讲的方法之外还能想出什么方法?本节讲的方法不能把同一个变量自己跟自己交换,你的方法有没有什么局限性?