C 语言中的位运算符

在 C 语言中,相同的数字可以用不同的数制来表示。也就是十进制的数字可以等价的表示为二进制或者十六进制。那么对于二进制来说,可以进行逐个数字之间,也就是每一个数字位的运算。这种运算也广泛的存在我们日程使用的数字电路中。其实计算机的运算原理最底层就是位运算,也就是 0 和 1 的运算。

1. 位运算符

运算符 作用 示例
& 位与 a&b
| 位或 a|b
^ 位异或 a^b
~ 位非 ~b
<< 位左移 a<<b
>> 位右移 a>>b

对于位运算中的与、或、异或可以通过下面的表格来阐明。

x y x & y x | y x ^ y
0 0 0 0 0
0 1 0 1 1
1 0 0 1 1
1 1 1 1 0

对于非操作符有下表的结果。

x ~x
0 1
1 0

移位操作就是将位向左或者向右移动,空位用 0 来补齐。

2. 示例

# include <stdio.h>

int main()
{
    int x,y,z;
    x=10; // 10 = 1010
    y=15; // 15 = 1111
    z=x&y;
    printf("x & y = %d\n", z);
    z=x|y;
    printf("x | y = %d\n", z);
    z=x^y;
    printf("x ^ y = %d\n", z);
    z=~x;
    printf("~ x = %d\n", z);
    z=~y;
    printf("~ y = %d\n", z);
    z=x<<2;
    printf("x << 2 = %d\n", z);
    z=y>>2;
    printf("x >> 2 = %d\n", z);
    return 0;
}

运行结果如下:

x & y = 10
x | y = 15
x ^ y = 5
~ x = -11
~ y = -16
x << 2 = 40
x >> 2 = 3

那么我们分析一下这些结果。

     10 = 1 0 1 0
     15 = 1 1 1 1
10 & 15 = 1 0 1 0

按照上节的表格计算后,发现 10 与 15 进行位与计算后,结果为 10 。

     10 = 1 0 1 0
     15 = 1 1 1 1
10 | 15 = 1 1 1 1

按照上节的表格计算后,发现 10 与 15 进行位或计算后,结果为 15 。

     10 = 1 0 1 0
     15 = 1 1 1 1
10 ^ 15 = 0 1 0 1

按照上节的表格计算后,发现 10 与 15 进行位异或计算后,结果为 5 。

  10 = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1 0
~ 10 = 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 0 1

因为一个整数是由 4 个字节组成,每个字节是 8 位,因此
在 1010 前还有 28 个 0 存在。将这些 0 全部变为 1 ,这时的数字代表 -11。

  15 = 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1
~ 15 = 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0

在 1111 前还有 28 个 0 存在。将这些 0 全部变为 1 ,这时的数字代表 -40。

     10 =     1 0 1 0
10 << 2 = 1 0 1 0 0 0

把 10 向左移动两位,右面的空余位置用 0 补齐。

Tips:请特别注意,在向左移位的过程中,如果左移的位数超出数据的存储最大位数,那么将产生错误。

下面的示例程序展示了这种错误。

# include <stdio.h>

int main()
{
    int x,y,z;
    x=10; // 10 = 1010
    z=x<<200;
    printf("x << 200 = %d\n", z);
    return 0;
}

显然超过了 int 类型可以表示的最大位数。

在编译的时候,会出现如下的错误。

test.c: In function ‘main’:
test.c:7:8: warning: left shift count >= width of type [-Wshift-count-overflow]
     z=x<<200;
     15 = 1 1 1 1
15 >> 2 = 0 0 1 1

把 15 向右移动两位,左面的空位用 0 补齐。

3. 小结

位运算作为一种直接的,符合数字电路逻辑的运算,广泛的存在于我们的生活中。在编程语言中,通过位运算可以方便的获得如网络地址的计算,还有我们日常的一些加减乘除都是可以通过位运算来实现的。只不过很多运算由于表示不直观,容易出错,所以还是使用了普通的算数运算符等来进行计算。

同时也要区分,位运算与我们介绍的逻辑运算符很相似,所以请大家注意区分。