Skip to content

5 位运算

二进制位运算

(注:以下例子如无特别标注均为 short 类型)

按位与 "&"

同 1 为 1,否则为 0

也可以理解为 有 0 则 0 or &1 得原值

0&0=0 0&1=0 1&0=0 1&1=1

用途

  1. 取指定位
  • short x 的低 8 位: x & 0x00ff0x00ff 为二进制 0000 0000 1111 1111
  • short x 的高 8 位: x & 0xff000xff00 为二进制 1111 1111 0000 0000
  • 判断 short x 的第 3 位是否为 1 : x& 0x080x08 为二进制 0000 1000

TIP

取第 x 位即 第 x 位 & 1 其余位 & 0

判断第 x 位是否为 1 即 第 x 位 & 1 其余位 & 0

16 进制中 f 表示 11110 表示 0000

WARNING

二进制位数从右往左数分别是 第 0 位 第 1 位 第 2 位 第 3 位 以此类推

  1. 将指定位清零
  • short x 的低 8 位清零: x & 0xff00
  • short x 的高 8 位清零: x & 0x00ff
  • short x 的第 8 位清零: x & 0xffef

TIP

清零第 x 位 就让 第 x 位 & 0 其余位 & 1

按位或 "|"

有 1 为 1,否则为 0

0|0=0 0|1=1 1|0=0 1|1=1

用途

将指定位 置 1,其余位不变 如 x = x|0x00ff or x|= 0x00ff

TIP

将第 x 位 置 1,就让 第 x 位 |1

按位异或 "^"

相同为 0,不同为 1

0^0=0 0^1=1 ,1^0=1 1^1=0

用途

  1. 某些位取反

    • 低字节取反,高字节不变 x = x^0x00ff
    • 高字节取反,低字节不变 x = x^0xff00
    • 第 0 位和第 4 位取反,其余位不变 x = x^0x0011

    TIP

    将第 x 位取反,就将第 x 位 ^1

  2. 变量清零

    x = x^x

  3. x y 均为整型,则 (x^y)^y = x

  4. x y 均为整型,可交换变量 x = x^y; y = x^y; x= x^y;

按位取反 "~"

~0 == 1, ~1 == 0

只有这个用途了吗?取反后的数和原数有什么关系呢?

我们之前讲了进制与编码,不难发现

按位取反后得到的是这个数的反码,反码 + 1 = 相反数的补码

故可知 一个数按位取反 = 该数的相反数 - 1

左移 "<<"

向左移动 n 位,多余的高位丢弃,低位补 0

x<<3 将 x 的二进制位左移 3 位

右移 ">>"

向右移动 n 位,多余的低位丢弃,高位遵循以下规则增补

  1. 若右移对象为 无符号整数,高位补 0
  2. 若为 整型 或 字符型 :
    • 最高位为 0 时补 0,
    • 最高位为 1 时,若编译系统采用 “算数右移则高位补 1,若编译系统采用 “逻辑右移则高位补 0.

WARNING

在 32 位微软编译系统中,采用 “算术右移” 的形式

位段

大家应该都接触过 bool 型变量,只存储 0 与 1,但是否想过 bool 型所占空间是多少呢?

答案是 1 字节,即 8 位。

这时候就有人会问了,欸 0 与 1 的存储不是只需要占用 1 位吗,为什么需要用到 8 位呢?

很简单,因为 C 语言对内存的存取大部分情况下使用的是指针,但指针只能精确到字节,无法精确到位。所以 bool 类型的空间占用远超实际占用,只能使用最小字节单位。

怎么解决这个问题呢?C 语言为此提供了位段(bit-field)操作。

如何定义 位段?

与结构体类似的定义方式

C
struct name {
    member_list
};

e.g.struct packed_d {
    unsigned short f1 : 1;
    unsigned short f2 : 2;
    unsigned short f3 : 4;
}

如此定义,f1 占用 1 个二进制位,f2 占用 2 个二进制位,f3 占用 4 个二进制位,以上位段共需占用 1 字节。

定义了结构后便可定义位段结构类型的变量, struct packed_d x,y

如何使用?

与结构体类似 x.f1=1; x.f2=3;

WARNING

所赋值需考虑所占用的二进制位数,若超出则取低位舍高位

注意

  1. 位段成员需为 unsigned 型

  2. 可定义无名位段 如下所示,无名位段起占位作用。若无名位段宽度为 0,则表明下一位段从一个新字节开始存放 如下所示,该位段结构占 2 字节。

    C
    struct {
        unsigned short f1 : 1;
        unsigned short f2 : 2;
        unsigned short : 1;
        unsigned short f3 : 1;
        unsigned short : 0;
        unsigned short f4 : 3;
    }
  3. 位段成员所占二进制位数不超过编译器字长

  4. 位段不能说明为数组,也不可用指针指向位段成员。

  5. 不可用 sizeof() 求位段成员大小

  6. 定义位段结构类型时,可包含非位段成员

  7. 结构体类型变量的位段成员可在一般表达式中被引用,并自动转换为对应整数。