Skip to main content

01 | 位运算按位取反

程序中所有的数在计算机内存中都是以二进制的形式存储。二进制在内存中又是以补码的形式存放。位运算对 CPU 比较友好,是一种程序优化的手段。 位运算有:

  • 取反 ~
  • 按位或 |
  • 按位异或 ^
  • 按位与 &

对于比特位(bit)还可以进行移位:

  • 左移运算:向左进行移位操作,高位丢弃,低位补 0.
  • 右移运算:向右进行移位操作,对无符号数,高位补 0;对于有符号位,最高位补符号位

在 C/C++中一个 char 变量占 1 个字节,即 8 个bit,其实每一个 bit 都可以当前一个开关来用,以此来做标志位。

1. 基础位运算:#

1.1 按位与 &#

两个位都是 1,结果才会 1,否则为 0,例如:

    0000 1001&   1001 1010-------------    0000 1000

1.2 按位或 |#

两个位都是 0 时,结果才会 0,否则为 1,例如:

    0000 1001|   1001 1010-------------    1001 1011

1.3 按位或 |#

两个维护都是 0 时,结果才会 0,否则为 1,例如:

    0000 1001^   1001 1010-------------    1001 1011

1.4 取反 ~#

0 变 1,1 变 0

~   1001 1010-------------    0110 0101

1.5 左移运算 <<#

向左进行移位操作,高位丢弃,低位补 0。例如:

int a = 8a << 3移位前 0000 0000 0000 0000 0000 1000移位后 0000 0000 0000 0000 0100 0000

1.6 右移运算 >>#

向右进行移位操作,对无符号数,高位补 0,对于有符号数,高位补符号位。例如:

unsigned int a = 8a >> 3移位前 0000 0000 0000 0000 0000 1000移位后 0000 0000 0000 0000 0000 0001    int a = -8a >> 3  移位前 1111 1111 1111 1111 1111 1000移位后 1111 1111 1111 1111 1111 1111

2. 按位取反#

首先要搞懂原码、补码、反码、取反。

  • 原码:10 进制变 2 进制,8 位。
    • 正数是二进制本身
    • 负数是符号位 1,数值部分去 X 绝对值的二进制。并标明符号位。
    • 最左一位为最高位,代表符号位。
  • 反码:取反
    • 正数的反码和补码相同
    • 负数的符号位为 1,其他位是原码取反
  • 补码:符号为不变,其他位取反
    • 正数的补码和原码、反码相同
    • 负数是符号位为 1,其他位是原码取反,末尾+1。或者说负数的补码是其绝对值反码未位加 1
  • 取反即使 0 变 1,1 变 0

为什么要有补码及其补码修复?因为计算机中减法是通过加一个负数处理的,而负数又是通过补码保存的。目的就是为了统一加法减法。 二进制在内存中的存放是以补码的形式存放的,

示例一: 正数 9

  原码           0000 1001  反码           0000 1001  补码           0000 1001  取反           1111 0110  符号位一起取反,这里是补码的取反  反码           1000 1001  符号位不变,其余各位取反  补码           1000 1010  负数的补码就是反码的末尾+1
最终结果是 1000 1010,最高位 1 代表负数,1010 = 10,所以最终结果-10

正数 5

原码           0000 0101反码           0000 0101补码           0000 0101取反           1111 1010  符号位一起取反,这里是补码的取反反码           1000 0101  符号位不变,其余各位取反补码           1000 0110  负数的补码就是反码的末尾+1
最终结果是 1000 0110,最高位 1 代表负数,0110 = 6,所以最终结果-6

负数 9

原码           1000 1001反码           1111 0110补码           1111 0111取反           0000 1000  符号位一起取反,这里是补码的取反反码           0000 1000  正数的反码、补码、原码相同补码           0000 1000  正数的补码、反码相同
最终结果是 0000 1000,最高位 0 代表正数,1000 = 8,所以最终结果8

负数-10

原码           1000 1010反码           1111 0101补码           1111 0110取反           0000 1001  符号位一起取反,这里是补码的取反反码           0000 1001  正数的反码、补码、原码相同补码           0000 1001  正数的补码、反码相同
最终结果是 0000 1001,最高位 0 代表正数,1001 = 9,所以最终结果9

按位取反友趣的事实#

  • 所有正整数的按位取反是其本身+1 的负数。
    • 10 按位取反 -11
    • 111 按位取反 -112
    • 1xxxx11 按位取反 -1xxxx12
  • 所有负整数的按位取反是其本身+1 的绝对值。
    • -6 按位取反 5
    • -111 按位取反 110
  • 0 的按位取反是-1。0 在数学界即不是正数也不是负数。