位运算

  程序中的所有数在计算机内存中都是以二进制的形式储存的。位运算就是直接对整数在内存中的二进制位进行操作。比如,and运算本来是一个逻辑运算符,但整数与整数之间也可以进行and运算。举个例子,6的二进制是110,11的二进制是1011,那么6 and 11的结果就是2,它是二进制对应位进行逻辑运算的结果(0表示False,1表示True,空位都当0处理)。

含义  Pascal c     java  
按位与 and & &
按位或 or | |
按位异或 xor ^ ^
按位取反 not ~ ~
按位左移 shl << <<
带符号右移 shr >> >>
无符号右移     >>>

运算符说明:

   1.按位与:   

     and运算通常用于二进制取位操作,例如一个数 and 1的结果就是取二进制的最末位。这可以用来判断一个整数的奇偶,二进制的最末位为0表示该数为偶数,最末位为1表示该数为奇数。

   2.按位或:

     or运算通常用于二进制特定位上的无条件赋值,例如一个数or 1的结果就是把二进制最末位强行变成1。如果需要把二进制最末位变成0,对这个数or 1之后再减一就可以了,其实际意义就是把这个数强行变成最接近的偶数。

   3.按位异或:

     异或的符号是^。按位异或运算, 对等长二进制模式按位或二进制数的每一位执行逻辑按位异或操作. 操作的结果是如果某位不同则该位为1, 否则该位为0.下面是一个交换例子:

       a=a ^ b;

       b=a ^ b;

       a=a ^ b;

   4.按位取反:

     ~运算的定义是把内存中的0和1全部取反。使用~运算时要格外小心,你需要注意整数类型有没有符号。如果~的对象是无符号整数(不能表示负数),那么得到的值就是它与该类型上界的差,因为无符号类型的数是用00到$FFFF依次表示的。如果~的对象是有符号的整数,写在后面.

   5.按位左移:

     a << b就表示把a转为二进制后左移b位(在后面添b个0)。例如100的二进制为1100100,而110010000转成十进制是400,那么100 << 2 = 400。可以看出,a << b的值实际上就是a乘以2的b次方,因为在二进制数后添一个0就相当于该数乘以2。

     可以用<<来定义Max_N等常量,例如 1<<16 -1表示65535  

    6.按位右移:

     和<<相似,a >>b表示二进制右移b位(去掉末b位),相当于a除以2的b次方(取整)。我们也经常用>> 1来代替div 2,比如二分查找、堆的插入操作等等。想办法用shr代替除法运算可以使程序效率大大提高。

    注意:左移:

      高位删除,低位补0,但是注意有时会溢出

       int i = 0x40000000; //16进制的40000000,为2进制的01000000...0000
       i = i << 1;
那么,i在左移1位之后就会变成0x80000000,也就是2进制的          100000...0000,符号位被置1,其他位全是0,变成了int类型所能表示的最小值,32位的               int这个值是-2147483648,溢出,若再左移一位则为0.

     右移:

     符号整数不影响,对于有符号整数符号位不变,低位删除

            int i = 0x80000000;
        i = i >> 1;  //i的值不会变成0x40000000,而会变成0xc0000000

      如何消除右移对符号的影响,如java的<<<

     1.用unsigned int强制转换

       2.与0x7fffffff与操作

#include<iostream>
using namespace std;
int main()
{
    short int a=0x8000;
    cout<<(a>>1)<<endl;
    cout<<((unsigned short int)a>>1)<<endl;
    cout<<((0x7fff)&(a>>1));
    return 0;
}

     

优先级

1      ~
2 <<,>>
3 &
4 ^
5 |
6 &=、^=、|=、<<=、>>=

简单事例

    去掉最后一位 | (101101->10110) |                 x >> 1
    在最后加一个0 | (101101->1011010) |             x << 1
    在最后加一个1 | (101101->1011011) |             x << 1+1
    把最后一位变成1 | (101100->101101) |              x | 1
    把最后一位变成0 | (101101->101100) |            x | 1-1
    最后一位取反 | (101101->101100) |               x ^ 1
    把右数第k位变成1 | (101001->101101,k=3) |           x | (1 << (k-1))
    把右数第k位变成0 | (101101->101001,k=3) |           x & ~ (1 << (k-1))
    右数第k位取反 | (101001->101101,k=3) |            x ^ (1 << (k-1))
    取末三位 | (1101101->101) |                  x & 7
    取末k位 | (1101101->1101,k=5) |                x & (1 << k-1)
    取右数第k位 | (1101101->1,k=4) |                x >> (k-1) & 1
    把末k位变成1 | (101001->101111,k=4) |            x | (1 << k-1)
    末k位取反 | (101001->100110,k=4) |               x ^ (1 << k-1)
    把右边连续的1变成0 | (100101111->100100000) |        x & (x+1)
    把右起第一个0变成1 | (100101111->100111111) |        x | (x+1)
    把右边连续的0变成1 | (11011000->11011111) |          x | (x-1)
    取右边连续的1 | (100101111->1111) |                                   x ^ (x+1)) >> 1
    去掉右起第一个1的左边 | (100101000->1000) |         x & (x ^ (x-1))(或 x & (-x))

 负数的位运算
    
 1 #include<stdio.h>
 2 int main()
 3 {
 4     short int a,b;
 5     a=0x0000;
 6     b=0x0001;
 7     printf("%d  %d
",a,b);
 8     a=0xFFFE;
 9     b=0xFFFF;
10     printf("%d  %d
",a,b);
11     a=0x7FFF;
12     b=0x8000;
13     printf("%d  %d
",a,b);
14     return 0;
15 }

 

  其中前两个数是内存值最小的数,中间两个数则是内存值最大的数,最后输出的两个数是正数与负数的分界处。二进制的第一位是用来表示正负号的,0表示正,1表示负。正数的原码,补码,反码都相同,都等于它本身.负数的补码是:符号位为1,其余各位求反,末位加1,反码是:符号位为1,其余各位求反,但末位不加1

 

 

原文地址:https://www.cnblogs.com/whzlw/p/4486618.html