6. 位运算符

位运算是指对整数在二进制位级别上进行的运算。

位操作经常用在嵌入式系统和 Linux 内核驱动程序中。

C 语言位运算的运算符有如下 6 种:

&     // 按位与运算
|     // 按位或运算
^     // 按位异或运算
<<    // 左移运算
>>    // 右移运算
~     // 按位取反运算

其中 &|^<<>> 是二元运算符,且左右两个操作数一定是整数。

语法格式如下:

左表达式 二元位运算符 右表达式

~ 是一元运算符

语法格式如下:

~ 表达式

6.1 按位与运算(&

按位与运算是两个操作数的对应位都为 1 时,该位结果才为 1;否则该位结果为 0

按位与运算的特点是一个位和 0 与则变为 0 , 和 1 与则不变。

示例

#include <stdio.h>

int main(int argc, char *argv[]) {
    short int x = 0x29;  // 00000000 00101001
    short int y = 0x27;  // 00000000 00100111
    short int z;

    z = x & y;
    printf("z: 0x%04x\n", z);

    return 0;
}

执行结果如下:

z: 0x0021

运算过程

z = x & y;

x:       00000000 00101001
y:    &  00000000 00100111
     --- -------- --------
z:       00000000 00100001

6.2 按位或运算(|

按位或运算是两个操作数的对应位都为 0 时,该位结果才为 0;否则该位结果为 1

按位或运算的特点是一个位和 1 或则变为 1 ,和 0 或则不变。

示例

#include <stdio.h>

int main(int argc, char *argv[]) {
    short int x = 0x29;  // 00000000 00101001
    short int y = 0x27;  // 00000000 00100111
    short int z;

    z = x | y;
    printf("z: 0x%04x\n", z);

    return 0;
}

执行结果如下:

z: 0x002f

运算过程

z = x | y;

x:       00000000 00101001
y:    |  00000000 00100111
     --- -------- --------
z:       00000000 00101111

6.3 按位异或运算(^

按位异或运算是两个操作数的对应位不相同时,该位结果为 1;相同该位结果为0

按位异或运算的特点是一个位和 1 异或则翻转(1001) , 和 0 异或则不变。

示例

#include <stdio.h>

int main(int argc, char *argv[]) {
    short int x = 0x29;  // 00000000 00101001
    short int y = 0x27;  // 00000000 00100111
    short int z;

    z = x ^ y;
    printf("z: 0x%04x\n", z);

    return 0;
}

执行结果如下:

z: 0x000e

运算过程

z = x ^ y;

x:       00000000 00101001
y:    ^  00000000 00100111
     --- -------- --------
z:       00000000 00001110

6.4 左移运算(<<

左移运算(<<)是将操作数的所有位向左侧移动指定的位数,高位溢出丢掉,低位补 0

示例

#include <stdio.h>

int main(int argc, char *argv[]) {
    short int x = 0x29;  // 00000000 00101001
    short int z;

    z = x << 1;
    printf("z: 0x%04x\n", z);

    z = x << 2;
    printf("z: 0x%04x\n", z);

    return 0;
}

执行结果如下:

z: 0x0052
z: 0x00a4

运算过程

z = x << 1;

x:       00000000 00101001
     <<  00000000 00000001
     --- -------- --------
z:    0  00000000 01010010  <-- 补1个 0
z = x << 2;

x:       00000000 00101001
     <<  00000000 00000010
     --- -------- --------
z:   00  00000000 10100100  <-- 补2个 0

6.5 右移运算(>>

右移运算(>>)是将操作数的所有位向右侧移动指定的位数,低位溢出丢掉,对于高位有这两种不同的填补方法:

示例

#include <stdio.h>

int main(int argc, char *argv[]) {
    short int x = 0x29;  // 00000000 00101001
    short int z;

    z = x >> 1;
    printf("z: 0x%04x\n", z);

    z = x >> 2;
    printf("z: 0x%04x\n", z);

    return 0;
}

执行结果如下:

z: 0x0014
z: 0x000a

运算过程

z = x >> 1;

x:       00000000 00101001
     >>  00000000 00000001
     --- -------- --------
z:       00000000 00010100  1(溢出1个位)
z = x >> 2;

x:       00000000 00101001
     >>  00000000 00000010
     --- -------- --------
z:       00000000 00001010  01(溢出2个位)

6.6 按位取反运算(~

按位取反运算(~)是将操作数对应的位翻转(1001)。

示例

#include <stdio.h>

int main(int argc, char *argv[]) {
    short int x = 0x29;  // 00000000 00101001
    short int z;

    z = ~x;  // z = 11111111 11010110
    printf("z: 0x%04x\n", z);

    return 0;
}

执行结果如下:

z: 0xffffffd6

运算过程

z = ~x;

x:    ~  00000000 00101001
     --- -------- --------
z:       11111111 11010110

练习1:

输入两个无符号的整数,计算两个整数位与、位或、位异或的值并打印结果。

练习2:

输入一个整数,用有符号变量 x 绑定。

  1. 将 x 的第 0 位置0 后打印结果。提示: x &= (~1);
  2. 将 x 的第 1 位置1 后打印结果。提示: x |= 0x01 << 1;
  3. 将 x 的第 2 位取反后打印结果。提示:任意为和 1 异或翻转,和 0 异或不变。