按位运算符的运用:
C语言中提供了6个位操作符。这些运算符只能作用于整型操作数,即只能作用于带符号或无符号的char、short、int与long类型:
& | ^ >> << ~
1.按位与的运用:&
提取特定位、清零其余位:
例如:mask中要保留的位上为1,其他位为0,a=a0&mask
判断int的奇偶(效率比%2高得多):
例如:(a&1)==0则为偶数,反之为奇数。(原理:因为奇数二进制末位总为1,偶数总为0。原数与00…001进行按位与运算,就得到了a二进制末位的值。)
2.按位异或运用:^
特定位取反
例如:mask中要取反的位为1,其余为0,a=a0^mask
不用中间变量交换两数的值
例如:要交换a、b的值只需:a=a^b;b=a^b;a=a^b;即可。
(原理:上式即a=(a^b)^(a^b)^b,b=a^b^b。由于一个数与它本身进行“按位异或”运算得到0,任何一个数与0进行“按位异或”运算得到它本身,故上式即是:a=[b的原值],b=[a的原值]。这样就达到了交换a、b的目的。)
下面用一个函数来说明位运算符的操作:
getbits(x, p, n);它返回x中从右边数第p位开始向右数n位的字段。这里假定最右边的一位是第0位,n与p都是合理的正值。
例如:getbits(x, 4, 3)返回x中的第4、3、2三位的值。
/**********
Description:
getbits函数:返回x中从第p位开始的n位
****************/
unsigned getbits(unsigned x, int p, int n)
{
return (x >> (p+1-n)) & ~(~0 << n);
}
其中,表达式(x >> (p+1-n))将期望获得的字段移位到字的最右端。
~0的所有位都为1,这里使用语句~0 << n将~0左移n为,并将最右边的n位用0填补。
再使用~运算对它按位取反,这样就建立了最右边n位全为1的屏蔽码。
习题2-6。
编写一个函数setbits(x, p, n ,y),该函数返回对x执行下列操作后的结果值:将x中从第p位开始的n个(二进制)位设置为y中最右边n位的值,x的其余各位保持不变。
/*************************
Description:
setbits:set n bits of x at position p with bits of y
*************************/
unsigned setbits(unsigned x, int p, int n, unsigned y)
{
return x & ~(~(~0 << n) << (p+1-n)) | (y & ~(~0 << n))<<(p+1-n);
}
分析:
为了把x中的n位设置为y最右边的n位的值
xxx...xnnnx...xxx x
yyy..........ynnn y
我们需要对x中的n位清零;把y中除最右边的n位以外的其他位都清零并左移到第p位处;然后对前面两步的结果值进行OR操作。如下所示:
xxx...x000x...xxx
000...0nnn0...000 OR操作
-----------------
xxx...xnnnx...xxx
为了对x中的n位清零,我们需要把x与一个屏蔽码进行AND操作。这个屏蔽码从位置p开始的n位都是0,其他位则全是1。
首先,把一个所有位都为1的屏蔽码左移n位,在它的最右边制造出n位0:
~0 << n
然后,把屏蔽码最右边的n位设置为1,把其余位全部设置为0:
~(~0 << n)
接下来,把屏蔽码最右边的n个1的位左移到第p位处:
~(~0 << n) << (p+1-n)
再往后,把屏蔽码从第p位开始的n位设置为0,把其余位全部设置为1
~(~(~0 << n) << (p+1-n))
用这个屏蔽码和x进行AND操作,就完成了对x从第p位开始的n位清零的工作。
x & ~(~(~0 << n) << (p+1-n));
为了把y中除最右端的n位以外的所有位清零,我们需要用最右端的n位全为1,其余位全为0的屏蔽码对y进行AND操作,如下所示:
~(~0 << n)
用这个屏蔽码和y进行AND操作,我们就选出了y最右端的n位。具体操作如下:
y & ~(~0 << n)
接下来,我们还需要用下面的操作把这n位左移到位置p处:
(y & ~(~0 << n)) << (p+1-n)
最后,对通过上述步骤得到的两个阶段性的结果进行OR操作:就完成了“将x中从第p位开始的n位设置为y中最右边n位的值,x的其余位保持不变”。
一个巧妙运用移位运算符的程序:(感觉面试中也经常出这样的题目)
输出int类型值中1的个数,或者用二进制表示出来;
#include<iostream>
using namespace std;
int main()
{
int a=13;
int s=0;
for(int i=1;i<=32;a<<=1,i++)
{
if(a<0)
s++;
cout<<(a<0);
}
cout<<endl<<"1的个数为:"<<s<<" 个"<<endl;
system("pause");
return 0;
}
执行结果:
声明int型变量时,默认的为signed,带符号位,所以可以这样应用。
循环32次才统计出多少个1来,如果仅仅是统计1的个数,效率就不算高了,《编程之美》上有同样的面试题目,给出了比较高效的算法,《C程序设计语言》里也有更高效的算法,后面再补上来,自己电脑遭人攻击,挂了,相当郁闷。
int、short和long类型默认均为带符号型的