位运算

二进制位运算#

(注:以下例子如无特别标注均为 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 )操作。

如何定义 位段 ?#

与结构体类似的定义方式

1
2
3
4
5
6
7
8
9
10
11
12
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字节。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    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. 结构体类型变量的位段成员可在一般表达式中被引用,并自动转换为对应整数。