在《C语言之结构体》的最后,我们提到了有关位域的概念,我们可以发现C语言强大到精准到位的控制能力。这篇我们就来讲讲位操作。
C语言不仅可以按位存储,也可以进行一些操作。在这之前,我们来回顾一下,C语言的逻辑运算符。
逻辑运算符 | 中文名 |
---|---|
&& | 且 |
|| | 或 |
! | 非 |
实际上,C语言的逻辑运算符是有返回值的,只不过这个返回值有些特殊,是布尔类型的(C++中才有)。而C语言中是没有明确规定存在布尔类型,所以C语言中就用底层来实现——1代表True,0代表False。
因此:
a = 50;
a == 50;
第二行的返回值就是1(True)
。
而:
a = 50; b = 100;
a == 50 && b == 50;
第二行的返回值就是0(False)
。
正因为是这种实现方式,因此在制造恒循环的时候,可以这么写:
while 1
1
这个表达式就相当于True
,等价于这个表达式永远都是返回True
,循环永远得以执行。
位运算
C语言中,位操作通常都是针对二进制。因此,和逻辑运算符有点类似:
位运算符 | 优先级 | 中文名 | 用法 |
---|---|---|---|
~ | 高 | 按位取反 | ~a |
& | 中 | 按位与 | a&b |
^ | 低 | 按位异或 | a^b |
| | 最低 | 按位或 | a|b |
按位取反
按位取反实际上就是 1 变 0,0 变 1。
举个例子:
按位与
按位与就是全 1 则 1,有 0 则 0。
举个例子:
按位异或
按位异或就是同则为 0,不同为 1。
举个例子:
按位或
按位或就是有 1 则 1,全 0 则 0。
举个例子:
和赋值号结合
除了按位取反是单目运算符,其余的三个都可以和赋值号结合,可以使代码更加简洁:
//Example 01
# include <stdio.h>
int main(void)
{
int plus = 0b11110000;
int ori1 = 0b11001100;
int ori2 = 0b11001100;
int ori3 = 0b11001100;
ori1 &= plus;
ori2 ^= plus;
ori3 |= plus;
printf("ori1 = 0x%x\n", ori1);
printf("ori2 = 0x%x\n", ori2);
printf("ori3 = 0x%x\n", ori3);
return 0;
}
结果为:
//Consequence 01
ori1 = 0xc0
ori2 = 0x3c
ori3 = 0xfc
转成二进制:
//Consequence 01 in Binary
ori1 = 0b11000000
ori2 = 0b00111100
ori3 = 0b11111100
移位运算符
除了上述的逻辑位运算以外,C语言还提供了移位运算符。
左移
左移运算符(<<)就是将二进制向左移动若干位。
比如:
0b11001101 << 2;//左移2位
可以看到,左边移出的数据直接舍弃,右边不足的用 0 补齐。
右移
右移运算符(>>)和左移同理。
比如:
0b11001101 >> 2;//右移2位
实际应用
由于二进制只有 0 和 1 两个数字,正好对应电路的“通”和“断”两种状态。因此在工控或家电领域使用十分广泛。而配合位操作则可以实现电路控制。不过你是没办法对其中的一个位进行单独的控制的,因此,在这之前,我们要先了解下掩码。
掩码
掩码就是一串数字,通过和目标数字的按位操作,来实现对一个或多个位的操作,但不影响其它位。
比如一个 8 核的CPU,我们要通过掩码来控制核心的开关:
现在要打开核心4,但是不能直接控制第 4 个 0,那就需要通过掩码来解决:
按位或运算时用的 00010000 就是我们说的掩码。
关闭位
假设我们现在想要关闭核心 4,那我们可以这样:
然后将取反的掩码:
有人可能会问了,为啥要多此一举来取反呢?直接将掩码设置为 11101111不就可以了吗?
这里其实是有个规定,就是掩码中的 1 通常都是对应被控制的那一位。
配合位操作,可以实现很多的玩法,这里就不一一列举了,自己可以去探索一下。