编程语言
首页 > 编程语言> > C++中的位运算

C++中的位运算

作者:互联网

位运算符

符号 含义
& 按位与
| 按位或
^ 按位异或
~ 取反
<< 左移
>> 右移

运算规则

与运算

“a&b”是指将参加运算的两个整数a和b,按二进制位进行“与”运算。

运算规则:0&0=0; 0&1=0; 1&0=0; 1&1=1;
即:两位同时为“1”,结果才为“1”,否则为0
例如:3&5 即 0000 0011& 0000 0101 = 0000 0001 因此,3&5的值得1。
另,负数按补码形式参加按位与运算。

按位与&比较实用的例子:

  1. 比如我们经常要用的是否被2整除,一般都写成if(n % 2 == 0)可以换成if((n&1) == 0)
  2. 按位与运算可以取出一个数中指定位。例如:要取出整数84从左边算起的第3、4、5、7、8位,只要执行84 & 59,因为84对应的二进制为01010100,59对应的二进制为0011101101010100 & 00111011= 00010000可知84从左边算起的第3、4、5、7、8位分别是0、1、0、0、0。
  3. 清零。如果想将一个单元清零,使其全部二进制位为0,只要与一个各位都为零的数值相与,结果为零。

或运算

参加运算的两个对象,按二进制位进行“或”运算。
运算规则:0|0=0; 0|1=1; 1|0=1; 1|1=1;
即 :参加运算的两个对象只要有一个为1,其值为1。
例如:3|5 即 00000011 | 0000 0101 = 00000111 因此,3|5的值得7。 
另,负数按补码形式参加按位或运算。

按位或 (|) 比较实用的例子

  1. 可以用一个unsigned int 来存储多个布尔值。比如一个文件有读权限,写权限,执行权限。看起来要记录3个布尔值。我们可以用一个unsigned int也可以完成任务。

  2. 一个数r来表示读权限,它只更改个位来记录读权限的布尔值00000001 (表示有读权限)00000000 (表示没有读权限)

  3. 一个数w表示写权限,它只用二进制的倒数第二位来记录布尔值00000010 (表示有写权限)00000000 (表示没有写权限)

  4. 一个数x表示执行权限,它只用倒数第三位来记录布尔值00000100 (表示有执行权限)00000000 (表示没有执行权限)

那么一个文件同时没有3种权限就是~r | ~ w | ~ x 即为 00000000,就是0
只有读的权限就是r | ~w | ~x 即为 00000001,就是1
只有写的权限就是~r | w | ~x 即为 00000010,就是2
一个文件同时有3种权限就是r | w | x 即为 00000111,就是7

异或运算

参加运算的两个数据,按二进制位进行“异或”运算。
运算规则:0 ^ 0=0; 0 ^ 1=1; 1^ 0=1; 1^1=0;
即:参加运算的两个对象,如果两个相应位为“异”(值不同),则该位结果为1,否则为0。

下面重点说一下按位异或,异或 其实就是不进位加法,如1+1=0,,0+0=0,1+0=1。
异或的几条性质:
1、交换律:a ^ b=b ^ a
2、结合律:(a ^ b) ^ c == a^ (b ^ c)

“异或运算”的特殊作用:
(1)使特定位翻转: 例:X=10101110,使X低4位翻转,用X ^ 0000 1111 = 1010 0001即可得到。
(2)与0相异或,保留原值 ,10101110^ 00000000 = 1010 1110。
(3)对于任何数x都有――自反性:x^ x=0,x^ 0=x 例如:A^B ^ B = A
(4)交换二个数:a =a ^ b; b = b ^ a; a = a ^ b;

按位异或应用举例1:
给出 n 个整数,n 为奇数,其中有且仅有一个数出现了奇数次,其余的数都出现了偶数次。用线性时间复杂度、常数空间复杂度找出出现了奇数次的那个数。
【输入样例】
9
3 3 7 2 4 2 5 5 4
【输出样例】
7

#include<bits/stdc++.h>
using namespace std;
int main()
{   int i,n,m,a;
    cin>>n;
    cin>>a;
    for(int i = 2; i <= n; i++) 
    {cin>>m; a^=m;   }
    cout<<a<<endl;
}

按位异或 应用举例2:
1-1000放在含有1001个元素的数组中,只有唯一的一个元素值重复,其它均只出现
一次。每个数组元素只能访问一次,设计一个算法,将它找出来;不用辅助存储空
间,能否设计一个算法实现?
解法一、显然已经有人提出了一个比较精彩的解法,将所有数加起来,减去1+2+...+1000的和。
这个算法已经足够完美了,相信出题者的标准答案也就是这个算法,唯一的问题是,如果数列过大,则可能会导致溢出。
解法二、异或就没有这个问题,并且性能更好。
将所有的数全部异或,得到的结果与123...1000的结果进行异或,得到的结果就是重复数。

#include<bits/stdc++.h>
using namespace std;
int main()
{  int i,n,a[11]={1,2,5,3,4,5,6,7,8,9,10};
   n=a[0]^a[1];
  for(i=2;i<=10;i++)n=n^a[i]; 
  for(i=1;i<=10;i++)n=n^i;
  cout<<n;
}

按位异或 应用举例3:
一系列数中,除两个数外其他数字都出现过两次,求这两个数字,并且按照从小到大的顺序输出.例如 2 2 1 1 3 4.最后输出的就是3 和4

#include<bits/stdc++.h>
using namespace std;
int a[1000];
int main()
{       int n;
        scanf("%d", &n);
        int x = 0;
        for(int i = 1; i <= n; i++) {      scanf("%d", &a[i]); x ^= a[i];    }
        int num1 = 0, num2 = 0;
        int tmp = 1;
        while(!(tmp & x)) tmp <<= 1;
        cout<<tmp<<endl;
        for(int i = 1; i <= n; i++) {
            if(tmp & a[i]) num1 ^= a[i];
            else num2 ^= a[i];
        }
        printf("%d %d\n", min(num1, num2), max(num1, num2));
    return 0;
}

取反

按位取反运算符(~)是指将整数的各个二进制位都取反,即1变为0,0变为1。

例如,~9=-10,因为9(00001001)所有位取反即为(11110110),这个数最高位是1,所以是补码。补码还原成反码(反码等于补码减1)得到(11110101),再还原为原码(反码到原码最高位不变,其它各位取反)等于(10001010), 十进制为-10。

左移

左移运算符是用来将一个数的各二进制位左移若干位,移动的位数由右操作数指定(右操作数必须是非负值),其右边空出的位用0填补,高位左移溢出则舍弃该高位。

在高位没有1的情况下,左移1位相当于该数乘以2,左移2位相当于该数乘以2*2=4,15<<2=60,即乘了4。
但此结论只适用于该数左移时被溢出舍弃的高位中不包含1的情况。

例如:143<<2 结果为60 因为143转换为进制为10001111,左移2得00111100 ,结果为60。

右移

左移运算符是用来将一个数的各二进制位左移若干位,移动的位数由右操作数指定(右操作数必须是非负值),其右边空出的位用0填补,高位左移溢出则舍弃该高位。

在高位没有1的情况下,左移1位相当于该数乘以2,左移2位相当于该数乘以2*2=4,15<<2=60,即乘了4。
但此结论只适用于该数左移时被溢出舍弃的高位中不包含1的情况。

例如:143<<2 结果为60 因为143转换为进制为10001111,左移2得00111100 ,结果为60。

不同长度的数据进行位运算

如果两个不同长度的数据进行位运算时,系统会将二者按右端对齐,然后进行位运算。

以“与”运算为例说明如下:如果一个4个字节的数据与一个2个字节数据进行“与”运算,右端对齐后,左边不足的位依下面三种情况补足:

(1)如果整型数据为正数,左边补16个0。

(2)如果整型数据为负数,左边补16个1。

(3)如果整形数据为无符号数,左边也补16个0。

常见操作

去掉最后一位 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->101101,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))

最后一个会在树状数组中用到

标签:运算,int,左移,C++,异或,按位,权限
来源: https://www.cnblogs.com/java-six/p/16175895.html