位运算符:
1.表达式:X&(X-1)
解释:每执行一次x = x&(x-1),会将x用二进制表示时最右边的一个1变为0,因为x-1将会将该位(x用二进制表示时最右边的那个1)变为0。(假设该位为第k位,减1后,该位后面全部变成1,但是由于该位是最右边的1了,所以x的第k位后面全是0,而x-1的第k位是0,从1到k-1位全是1,按位与的结果自然就是第k位和1~k-1位都变成0。
实际应用1:将一个数变成二进制代码后统计其中含有的1的位数,代码如下:
int func(int x)
{
int countx = 0;
while(x)
{
countx++;
x = x&(x-1); //每次将x最右边的1变成0
}
return countx;
}
假定x = 9999
10011100001111
实际应用2: 当x为奇数的时候,x=x&(x-1)它的值相当于x=x-1;同样,当x为2的N次幂时,结果为0,这可以用来快速判断一个数是否为2的n次方,代码如下:
int func(int x)
{
if( (x&(x-1)) == 0 ) //2的n次方有个很明显的特点是其转换成2进制后只有一位为1,其他位都为0;
return 1;
else
return 0;
}
解释:该表达式真是目的是运用位运算求取平均值,代码如下:
int average(int x,int y)
{
return ( (x&y) + ( (x^y)>>1 ) );
}
我们可以这样来考虑,将x和y的值分别拆成两部分进行相加,有如下两种情况:
1:x和y对应位相同:这种情况下,x和y的值相等,x&y的值等于x或y,也等于(x+y)/2;
2:x和y对应位不同:这种情况下,x和y其中一个为0,一个为1,(x^y)>>1就等于二者相加的结果去平均值,如0010和0000,(x^y)>>1= 0001,等于(2+0)/2。
3.表达式:(x^y)+((x&y)<<1)
解释:通过该表达式可以实现利用位运算实现加法运算,代码如下:
int Add(int a,int b)
{
if(b == 0) //b等于0,说明不会有进位,直接等于a
return a;
int sum,carry;
sum = a ^ b; //第一步<span style="font-family: Arial;">完成</span><span style="font-family: Arial;">没有进位的加法运算</span>
carry = (a & b) << 1; //第二步完成有进位的加法运算
return Add(sum,carry); //迭代,直到没有进位
}
举个简单的例子,完成10+6的运算,有以下几个步骤:
第一次迭代:sum = 10^6 = 12; carry = (10&6)<<1 = 4。
第二次迭代:sum = 12^4 = 8; carry = (12&4)<<1 = 8。
第三次迭代:sum = 8^8 = 0; carry = (8&8)<<1 = 16。
第四次迭代:sum = 0^16 = 16; carry = (0&16)<<1 = 0;
结束(有几位二进制数即需要迭代几次)。
这里详细的内容可参考博客:http://blog.youkuaiyun.com/zhongjiekangping/article/details/6855864
4.需要注意的一个例子:
int _tmain(int argc, _TCHAR* argv[])
{
unsigned char a = 0xa5;
unsigned char b = ~a >> 4+1;
printf("b=%d\n",b);
return 0;
}
这里输出的结果是250,而不是我所想的2,其原因是在计算这个表达式的时候,编译器会先把a和4的值转换成int类型(即所谓的整数提升),然后再进行计算,当计算结果出来后,再把结果转换成unsigned char 赋值给b.
本题的运算顺序如下:a = 0000 0000 1010 0101,由于按位取反运算符优先级大于加运算符大于移位运算符,所以先对a进行按位取反操作,然后执行4+1,再将a按位取反的值向右移动5位,得出结果。
附录运算符优先级表格: