深入总结嵌入式位操作运算符

位运算应用法则

清零取位要用与,某位置1要用或,取反和交换用异或

移位运算要诀

1.他们都是双目运算符,两个运算分量都是整形,结果也是整形。 
2.“<<”左移:右边空出的位上补0,左边的位将从字头挤掉,其值相当于乘2; 
3“>>”右移:右边的位被挤掉了,对于左边移出的空位,如果是正数则空位补0,若为负数,可能补0或1,这取决于不同的计算系统。 
4“>>>”运算符:右边的位被挤掉,对于左边移出的空位一概补上0;

位运算的应用(源操作数a 掩码b)

(1)按位与& 
1.清除某特定位(b中特定位置0,其余位为1,a=a&b) 
2.取某数中指定位(b中特定位置1,其余位为0,a=a&b) 
(2)按位或--| 
常用来将源操作数某些位置1,其它位置不变。(b中特定位置1,其余位为0,a=a|b) 
(3)位异或--^ 
相同为0,不同为1 
1 使特定位的值取反 
例如对数10100001的第2位和第3位翻转,则可以将该数与00000110进行按位异或运算。 
      10100001^00000110 = 10100111 
2 不引入第三变量,交换两个变量的值 
例如交换两个整数a=10100001,b=00000110的值,可通过下列语句实现: 
    a = a^b;   //a=10100111 
    b = b^a;   //b=10100001 
    a = a^b;   //a=00000110 
3.快速判断两个值是否相等 
举例1: 判断两个整数a,b是否相等,则可通过下列语句实现: 
return ((a ^ b) == 0) 
举例2: Linux中最初的ipv6_addr_equal()函数的实现如下: 
static inline int ipv6_addr_equal(const struct in6_addr *a1, const struct in6_addr *a2) 

return (a1->s6_addr32[0] == a2->s6_addr32[0] && 
a1->s6_addr32[1] == a2->s6_addr32[1] && 
a1->s6_addr32[2] == a2->s6_addr32[2] && 
a1->s6_addr32[3] == a2->s6_addr32[3]); 
}

可以利用按位异或实现快速比较, 最新的实现已经修改为:
static inline int ipv6_addr_equal(const struct in6_addr *a1, const struct in6_addr *a2)
{
return (((a1->s6_addr32[0] ^ a2->s6_addr32[0]) |
    (a1->s6_addr32[1] ^ a2->s6_addr32[1]) |
    (a1->s6_addr32[2] ^ a2->s6_addr32[2]) |
    (a1->s6_addr32[3] ^ a2->s6_addr32[3])) == 0);
}   

4.基于按位异或的加密解密算法 
class E 
{ public static void main(String args[ ]) 

char a1='十' , a2='点' , a3='进' , a4='攻' ; 
char secret='8' ; 
a1=(char) (a1^secret); 
a2=(char) (a2^secret); 
a3=(char) (a3^secret); 
a4=(char) (a4^secret); 
System.out.println("密文:"+a1+a2+a3+a4); 
a1=(char) (a1^secret); 
a2=(char) (a2^secret); 
a3=(char) (a3^secret); 
a4=(char) (a4^secret); 
System.out.println("原文:"+a1+a2+a3+a4); 


如果两个数相应的位上一样,结果就是0,不一样就是1 
所以111^101=010 
那加密的过程就是逐个字符跟那个secret字符异或运算. 
解密的过程就是密文再跟同一个字符异或运算 
010^101=111

二进制补码运算公式:

-x = ~x + 1 = ~(x-1) 
~x = -x- 
-(~x) = x+ 
~(-x) = x- 
x+y = x - ~y - 1 = (x|y)+(x&y) 
x-y = x + ~y + 1 = (x|~y)-(~x&y) 
x^y = (x|y)-(x&y) 
x|y = (x&~y)+y 
x&y = (~x|y)-~x 
x==y: ~(x-y|y-x) 
x!=y: x-y|y-x 
x< y: (x-y)^((x^y)&((x-y)^x)) 
x<=y: (x|~y)&((x^y)|~(y-x)) 
x< y: (~x&y)|((~x|y)&(x-y))//无符号x,y比较 
x<=y: (~x|y)&((x^y)|~(y-x))//无符号x,y比较

应用举例

(1) 判断int型变量a是奇数还是偶数 
a&1 = 0 偶数 
a&1 = 1 奇数 
(2) 取int型变量a的第k位 (k=0,1,2……sizeof(int)),即a>>k& 
(3) 将int型变量a的第k位清0,即a=a&~(1< (4) 将int型变量a的第k位置1, 即a=a|(1< (5) int型变量循环左移k次,即a=a<>16-k (设sizeof(int)=16) 
(6) int型变量a循环右移k次,即a=a>>k|a<<16-k (设sizeof(int)=16) 
(7)整数的平均值 
对于两个整数x,y,如果用 (x+y)/2 求平均值,会产生溢出,因为 x+y 可能会大于INT_MAX,但是我们知道它们的平均值是肯定不会溢出的,我们用如下算法:

 int average(int x, int y)   //返回X,Y 的平均值
{   
 return (x&y)+((x^y)>>1);
 }

(8)判断一个整数是不是2的幂,对于一个数 x >= 0,判断他是不是2的幂

boolean power2(int x) 

return ((x&(x-1))==0)&&(x!=0); 
}

(9)计算绝对值

int abs( int x ) 

int y ; 
y = x >> 31 ; 
return (x^y)-y ; //or: (x+y)^y 

(10)取模运算转化成位运算 (在不产生溢出的情况下):a % (2^n) 等价于 a & (2^n - 1) 
(11)乘法运算转化成位运算 (在不产生溢出的情况下):a * (2^n) 等价于 a<< n 
右边空出的位上补0,左边的位将从字头挤掉,其值相当于乘2; 
(12)除法运算转化成位运算 (在不产生溢出的情况下):a / (2^n) 等价于 a>> n 
右边的位被挤掉。对于左边移出的空位,补零,其值相当于除2; 
例: 12/8 == 12>>3 
(13) a % 2 等价于 a & 1 
(14) if (x == a) x= b; 
  else x= a; 
  等价于 x= a ^ b ^ x; 
(15) x 的 相反数表示为 (~x+1) 
(16) 实现最低n位为1,其余位为0的位串信息:~(~0 << n) 
(17)截取变量x自p位开始的右边n位的信息:(x >> (1+p-n)) & ~(~0 << n) 
(1)截取old变量第row位,并将该位信息装配到变量new的第15-k位:new |= ((old >> row) & 1) << (15 – k) 
(19)设s不等于全0,代码寻找最右边为1的位的序号j: 
for(j = 0; ((1 << j) & s) == 0; j++) ; 
return j;//由于1每次左移过程中只要遇到1此时相与为1,返回此时的位置J

你可能感兴趣的:(51单片机)