位运算 - 基础篇

位运算 - 缘起

        • 基本概念
            • 位操作
            • 补码
        • 位运算符
            • ¬取反 (NOT)
            • & 按位与 (AND)
            • | 按位或 (OR)
            • ^ 按位异或 XOR
            • 左移运算符
            • 右移运算符
            • 复合赋值运算

基本概念

位操作

Bit Manipulation, 程序运算中对位模式或二进制数的一元或二元操作。说白了,就是直接对整数的二进制进行运算操作。
在计算机的世界中,一切都是二进制机器码。熟练掌握位操作,有助于我们更好的理解程序运行原理。

在以往古老的微处理器中,位运算比加减运算略快,比乘除运算要快很多。在现代架构中,情况有所改变,位运算和加减运算速度接近相同,但仍然快于乘除运算。


补码

将一个十进制正数转换为二进制的时候,只需要通过除2取余的方法即可,比如:2 = 0010 但是将一个十进制负数转为二进制?
负数是以补码的形式表示,其转换方式,简单来讲:先按正数转换,然后取反加1. -2 = ~2 + 1 = 1101 + 0001 = 1110 (符号位 1)。

例如:十进制 -10

        10          0000 0000 0000 1010
        取反        1111 1111 1111 0101
        加1         1111 1111 1111 0110
        -10         1111 1111 1111 0110
        
        正数X       -x = ~x + 1

位运算符

¬取反 (NOT)

取反是一元运算符,对一个二进制数的每一位执行逻辑取反的操作。

运算规则:使数字1变成0,0变成1.

C/C++ 中使用 ‘~’做为取反运算符

例如:

        NOT 0111 (十进制 7)  = ... 1000 (十进制 -8)
        
                ~    0000 0111      7
                ----------------------
                     1111 1000      -8
     /* 
     *  注意:许多程序语言中使用"~"作为取反的操作符号.需要注意的是取反(~)操作和逻辑非(!)操作符不同,
     *  在C++程序中,逻辑非将数字整体看成一个布尔类型--将真值转化为假值。逻辑非并非一个位运算符。
     */ 

主要用途

1. 使一个数的最低位变为0
    // a 的最低位变为 0:  a & ~1, ~1的二进制除了末尾变成0,其余的位全是1.
2. ~运算符的优先级比算术运算符、关系运算符、逻辑运算符及其他运算符的优先级都高
& 按位与 (AND)

同1为1,同0为0。

运算规则:非1跟1按位与保持不变,1跟1按位与为1,跟0按位与清零。

C/C++ 中使用 ‘&’ 做为按位与运算符

例如:

        0110 (十进制 6) AND 1010 (十进制 10) = 0010 (十进制 2)
        
                    0000 0110       6
                 &  0000 1010       10
                 ----------------------
                    0000 0010       2
     /*
     *  按位与操作是比较常见的运算符,如果在LeetCode刷题的话,位运算很多题中会使用到按位与操作。
     *  1 & 1 = 1 | 1 & 0 = 0 | 0 & 1 = 0 | 0 & 0 = 0
     */

主要用途

1. 清零
    // 如果将一个数清零,即使其二进制位全变为0,只要与一个各位都为零的数值进行按位与运算,结果为零
2. 取一个数的指定位
    // 比如取一个数的后四位, X = 1011 0110,只要 0000 1111 进行与运算,这样可以获取最后4位的值:0110.
3. 判断奇偶
    // 最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if ((a & 1) == 0)代替if (a % 2 == 0)来判断a是不是偶数
4. 求一个数最低位1
    //  int val = x & (~x + 1); 如 x = 0001 1100, ~x = 1110 0011, ~x + 1 = 1110 0100, val = 0000 0100  
5. 2的幂次方
    // X & (X - 1) == 0, X为2的幂次方。
| 按位或 (OR)

遇1为1, 即参加运算的两个对象中只要有一个是1,结果为1

运算规则: 0 | 0 = 0 , 1 | 1 = 1 , 0 | 1 = 1, 1 | 0 = 1

负数按补码的形式参加按位与运算。

例如

            0010 (十进制 2) |  1010 (十进制 10)  = 1010 (十进制 10)
            
                0000 0010   2
             |  0000 1010   10
            --------------------
                0000 1010   10

主要用途

1. 对一个数据的某些位设置为1
    // 如 X = 1101 1001 , 取数Y,Y的最低4位为:1111,(X|Y) = 1101 1111
^ 按位异或 XOR

不同为1, 相同为0

运算规则: 1 ^ 0 = 1, 0 ^ 1 = 1, 0 ^ 0 = 0, 1 ^ 1 = 0

异或运算的性质:

1. 交换律   a ^ b = b ^ a
2. 结合律   (a ^ b) ^ c = a ^ (b ^ c)
3. 对与任何数X, 都有 X ^ X = 0, X ^ 0 = X
4. 自反律   a ^ b ^ b = a ^ 0 = a

例如

         0010 (十进制 2) ^ 0010 (十进制 2) = 0
         
            0000 0010   2
            0000 0010   2
        ^
        ---------------------
            0000 0000   0
            
        // leetCode题库中很多位运算,使用异或会让算法更简单直观.

主要用途

1. 翻转指定位数
// 如X = 1010 1110, 对X的个位进行翻转,取数 Y = 1111 1111 进行异或, (X ^ Y ) = 0101 0001

2. 与0异或,值不变。
// 如 X = 1010 1110 ^ 0000 0000 = 1010 1110

3. 交换两个数
void swap(int& a, int& b) {
    if (a !=b) {
        a ^= b;
        b ^= a;
        a ^= b;
    }
}
左移运算符

将一个数的各二进制位全部左移若干位,左边的位数遗弃,右边补0.

例如

     a = 1010 1100  左移动两位:1010 1011 0000  补零

如若左移的高位舍弃的不包括1,则每次左移一位,原数值增加2倍。即 a << 1 = a * 2

右移运算符

将一个数的各二进制位全部右移若干位,正数补0,负数补1,右边的丢弃。

例如

    a = 1010 1100   右移动两位: 0010 1011 正数补0,负数补1.

操作数右移1位,等价于操作数除2.

复合赋值运算

位运算和赋值运算符的组合,组成了新的复合运算符。

 &=         a &= b   -->     a = a & b;
 |=         a |= b   -->     a = a | b;
 >>=        a >>= b  -->     a = a >>= b;
 <<=        a <<= b  -->     a = a <<= b;
 ^=         a ^= b   -->     a = a ^= b;

本文对基本的位操作运算做了整理和说明,后续结合一些算法题,将会整理描述位运算的一些运算技巧和性质。时间允许的话,也希望能够把加减乘除也来一页做说明。

你可能感兴趣的:(C++基础知识,Linux,位运算,位运算)