C按位运算符

​ C按位运算符,重读后发现对后续写驱动很有用处,结合自己理解做记录

一、按位逻辑运算符

复习一下常见的四种按位逻辑运算符,和数电内容基本一致

1、二进制反码或按位取反:~

同数电非门,通过逐位比较两个运算对象,生成一个新值。

image-20230401204732155

1
2
~(10010001)	// 表达式
01101110 // 结果式

​ 若val的类型为unsigned char即uint8_t,值为00000010,十进制为2。那么,~val的值为11111101,十进制为253

1
2
// 使用参考
val = ~val;

2、按位与:&

同数电与门,通过逐位比较两个运算对象,生成一个新值。

image-20230401205520252

1
2
3
4
5
6
(10010001) & (01101110)  // 这里拿两个互为非的进行与运算
00000000 // 那不就是全为0嘛

(10010001) & (01100101) // 取两个不相关的值时
00000001 // 结果1

使用方法:

1
2
val &= 0156;	  // 0156是八进制,数字前带0是八进制,0x是十六进制
val = val & 0156; //二进制常量前面可以加上前缀 "0b" 或 "0B",但这并不是 C 语言的标准语法,因此不是所有编译器都支持这种写法。

3、按位或: |

同数电或门,通过逐位比较两个运算对象,生成一个新值。

image-20230401211052312

1
2
3
4
5
10010001 | 01101110  	 // 还是以拿两个互为非的进行或运算
11111111 // 全1了

(10010001) | (01100101) // 取两个不相关的值时
11110101 // 结果245

使用方法:

1
2
val |= 0156;		// 表达式
val = val | 0156; // 结果

4、按位异或: ^

同数电异或门,通过逐位比较两个运算对象,生成一个新值。

image-20230401212822464

1
2
3
4
5
10010001 ^ 01101110  	 // 还是以拿两个互为非的进行异或运算
11111111 // 全1了

(10010001) | (01100101) // 取两个不相关的值时
11110100 // 结果244

使用方法:

1
2
val ^= 0156;		// 表达式
val = val ^ 0156; // 结果245

二、用法

1、掩码

​ c语言按位与运算符常用于掩码(mark)

​ 掩码是一个二进制数字,用于屏蔽或标记二进制数中的一些位。在计算机科学中,掩码通常被用来表示或隐藏某些信息。通常情况下,掩码是一个二进制数字,用于与另一个二进制数进行按位运算,从而将原始数据中的一些位清除或保留下来。掩码的常见用途包括权限控制、数据加密和数据压缩等方面。

​ 掩码的应用非常广泛。在权限控制中,掩码被用来限制用户对特定资源的访问权限。例如,一个文件的权限掩码可能是一个 8 位二进制数,其中每一位代表一种访问权限,例如读、写和执行。在这种情况下,用户的权限也被表示为一个二进制数,用于与文件的权限掩码进行按位与运算,以确定用户是否具有访问权限。

​ 掩码还可以用于数据加密和解密。例如,在对数据进行加密时,一个掩码可以用来隐藏数据的一些位。只有拥有正确的掩码才能解密数据,从而确保数据的安全性。

​ 掩码还可以用于网络通信中。例如,在 IPv4 中,IP 地址和子网掩码都是 32 位的二进制数。子网掩码用来标识网络地址和主机地址的分隔符,用于确定特定 IP 地址所属的网络。当两个计算机在同一子网中进行通信时,掩码可以用来判断它们是否在同一网络中,从而决定是否需要进行路由。

​ 总的来说,掩码是一种十分重要的二进制技术,用于标记、保留或清除二进制数中的一些位。它在许多领域都有广泛的应用,包括权限控制、数据加密、网络通信等。

2、打开位(设置位)

​ 有时,需要打开一个值的特定位,同时保持其他位不变,这种情况可以使用按位或运算符(|)

1
2
3
4
5
6
// 标志位flags,目标是打开flags的第0位和第7位,而其他不变
int flags = 0xE; // 二进制表示为00001110
// 使用|运算符,任何位与0组合,结果都为其本身,任何位与1组合,结果都为1。
int mark = 0x81; // 二进制表示为10000001
// 将mark第0位和第七位设为1,其余为0,与flags进行或运算,则成功打开flags的第7位和第0位
flags |= mark; // 结果为10001111 = 0x8F = 143

同样,这种方法根据mark中为1的位,可以将flags中对应的位设置为1,其他位不变

3、关闭位(清空位)

​ 与打开位类似,有时需要关闭一个值的特定位,同时保持其他位不变,这种情况需要使用按与位运算符(&)和位或运算符(|)

1
2
3
4
5
6
// 标志位flags,目标是关闭flags的第1位,而其他不变
int flags = 0xE; // 二进制表示为00001110
// 先将mark要关闭的位设为1
int mark = 0x2; // 二进制表示为00000010
// ~mark将mark所有的0变为1,1变为0,值变为11111101,再进行&运算,进行与运算任何位与1组合都为其本身,与0组合为0
flags &= ~mark; // 结果为00001100 = 0xC = 12

同样,这种方法根据mark中为1的位,可以将flags中对应的位设置为0,其他位不变

4、切换位

​ 切换位的意思是:打开已关闭的位,或关闭已打开的位,这种情况可以使用异或运算符(^)

1
2
3
4
5
6
// 标志位flags,目标是切换flags的第1位和第5位,而其他不变
int flags = 0xE; // 二进制表示为00001110
// 先将mark要切换的位设为1
int mark = 0x22; // 二进制表示为00100010
// 如果b为1,1^b为0;如果b为0,1^b为1;无论b为0还是1,0^b的结果都是b
flags ^= mark; // 结果为00101100 = 0x2c = 44

同样,这种方法根据mark中为1的位,可以将flags中对应的位切换,其他位不变

5、检查位的值

​ 有时需要检查某位的值,这种情况可以使用与运算符(&)

1
2
3
4
5
6
7
// 目标是检查flags中2号位的值是否为1
int flags = 0xE; // 二进制表示为00001110
// 先将mark要检查的位设为1
int mark = 0x8; // 二进制表示为00000100
// 不能这样直接比较位的值!!!!!!!!!
if (flags == mark)
printf("hello!\n");

这样做即使flags的2号为为1,其他位的值也会导致比较结果为false。因此,必须覆盖flags中的其他值,只用2号为和mark比较

1
2
3
4
// 先&运算后,flags除了mark位为1的相同位,其他位都与0位做与运算,都变为0了,只有与mark位为1的位保留原来的数据
// flags&mark运算后结果为00000100,此时再与mark比较,若返回ture则检查的位打开,返回false则检查的位关闭
if ((flags & mark) == mark)
printf("hello!\n");

重要提示

为了避免信息漏过便捷,线吗至少要与其覆盖的值的宽度相同