转载自:
http://zhan.renren.com/programming4idiots?gid=3602888498024183474&checked=true
估计大多数人第一次接触C语言位操作时都不屑一顾,很多人都会说“C是高级语言,还用得着在这么低级的数位层级上进行操作吗?这东西应该属于汇编才对!”我最早接触C语言的位操作符时也是这种态度。后来学习单片机时,先是用汇编写小程序,后来程序稍微大一点之后就改用C语言,结果位操作符知识的匮乏让我无比捉急,原本十分简单的移位寄存器程序就能让我折腾好几天。痛定思痛,还是决定认真看看位操作符的知识,并且整理成下面这份笔记。
值得一提地是,除了嵌入式系统开发外,即便是在PC机编程方面位操作符也有其用武之地,有很多非常巧妙的问题都可以通过位操作来方便地实现。此外,我们还可以注意到现在通用的高级语言一般都保留了位操作符,像Java这样的语言还在C的基础上有所加强(增加了>>>操作符,克服了右移时高位不确定的不足)。
C语言一共提供了6种位操作符:
&按位与
|按位或
^按位异或
~取反
<<左移
>>右移
1、&按位与:
&是二元操作符,参加运算的两个数据按二进制数位进行“与”(AND)运算。原则是全1才1,有0则0,即0&0=0、0&1=0、1&0=0、1&1=1。例如二进制下1100B&1001B=1000B,换成十进制就是12D&9D=8D。
&的应用有:
(1)、将某些位清零:
任何数同0相与的结果都是0,任何数同1相与都是该数自身,据此可以利用&来将某些位清零,例如:
01101101B&00001111B=00001101B // 将高四位清零,保留低四位不变
(2)、检测某一位的值:
原理同上,例如要检查某数的D4位是0还是1,可以将其与00010000B相与(待检测位是1,其它位是0),得到结果若是0,则原数D4位是0;结果若非零,则原数D4位是1。
2、|按位或:
|是二元操作符,参加运算的两个数据按二进制数位进行“或”(OR)运算。原则是全0才0,有1则1,即0|0=0、0|1=1、1|0=1、1|1=1。例如二进制下1100B|1001B=1101B,换成十进制就是12D|9D=13D。
|的应用有:
将某些位置1:
任何数同0相或的结果都是该数自身,任何数同1相或都是1,据此可以利用|来将某些位置1,例如:
01101101B|00001111B=01101111B // 将低四位置1,保留高四位不变
3、^按位异或:
^是二元操作符,参加运算的两个数据按二进制数位进行“异或”(XOR)运算。原则是两个位相同时为0,两个位不同时为1,即0^0=0、0^1=1、1^0=1、1^1=0。例如二进制下1100B^1001B=0101B,换成十进制就是12D^9D=5D。
^的应用有:
(1)、将某些位翻转:
任何数同0异或的结果就是该数自身,任何数同1异或就是该数的反,据此可以利用^来将某些位翻转,例如:
01101101B^00001111B=01100010B // 保留高四位不变,将低四位翻转
(2)、交换两个变量的值:
这应该是位操作最神奇的一个应用了,大家都知道在C语言中要交换两个变量的值一般都要用到第三个变量来临时寄存数值,然而异或操作却可以让大家在不声明第三变量的情况下完成数值交换。例如:
voidswap(int *pa, int *pb)
{
*pa=*pa^*pb;
*pb=*pa^*pb;
*pa=*pa^*pb;
}
用逻辑代数可以证明这种交换算法的正确性:易证异或运算满足交换律和结合律,则(a^b)^b=a^(b^b)=a^0=a,(a^b)^a=a^(a^b)=(a^a)^b=0^b=b。
4、~取反:
~是一元操作符,具有右结合性,其作用就是将操作数的每一位翻转,例如二进制下~1100B=0011B,换成十进制就是~12D=3D。
5、<<左移:
<<是二元操作符,其功能是把“<<”左边的运算数的各二进位全部左移若干位,由“<<”右边的数指定移动的位数,高位丢弃,低位补0。例如1100B<<1=1000B。左移一位相当于原数乘2。事实上,通过编译器生成的汇编指令可以看出CPU实际上就是用移位和迭代的方式完成了乘除运算。
6、>>左移:
>>是二元操作符,其功能是把“>>”左边的运算数的各二进位全部右移若干位,“>>”右边的数指定移动的位数,低位丢弃,高位补位情况与具体的编译器有关。例如1101B>>1可能等于0110B,也可能等于1110B。原则上,右移一位相当于原数除以2。