一、按位取反的概念
按位取反运算符为 ~,是一个单目运算符。其作用是对一个二进制数的每一位进行取反操作:0 变为 1,1 变为 0。
运算规则:
| 位a | ~a |
|---|---|
| 0 | 1 |
| 1 | 0 |
二、运算示例
假设有一个 unsigned char 类型的变量 a,其值为 0x4A(二进制 01001010),对其执行按位取反:
unsigned char a = 0x4A; // 01001010
unsigned char b = ~a; // 10110101,即 0xB5Code language: JavaScript (javascript)
分析:a 的每一位都被翻转,得到结果 0xB5。
注意:如果操作数是有符号类型,取反后的结果可能表示一个负数(取决于该类型的编码方式,通常为补码)。例如 char 类型(有符号)变量值为 5(00000101),取反后为 11111010,在补码表示下这是 -6。
三、常见用途
1. 配合按位与将指定位置零
按位取反常与按位与结合使用,用于构造掩码并将特定位清零。
示例:将 int 型变量 flags 的第 3 位(从 0 开始计数)清零,其余位保持不变。
flags = flags & ~(1 << 3);
分析:1 << 3 得到 00000000 00000000 00000000 00001000,取反后得到 11111111 11111111 11111111 11110111。再与 flags 按位与,即可将第 3 位清零,其余位不变。
2. 构造掩码
通过取反操作可以快速构造某些位模式。例如构造低 8 位全 1 的掩码:~0 在 32 位系统上会得到全 1,再根据需要移位或截断。
四、代码示例
以下代码演示了按位取反运算的实际应用:
#include <iostream>
using namespace std;
int main()
{
// 示例1:基本取反
unsigned char a = 0x4A; // 01001010
unsigned char b = ~a; // 10110101
cout << "a = " << hex << (int)a << endl; // 输出 4a
cout << "~a = " << hex << (int)b << endl; // 输出 b5
// 示例2:有符号数的取反
char c = 5; // 00000101
char d = ~c; // 11111010,补码表示 -6
cout << "c = " << dec << (int)c << endl; // 输出 5
cout << "~c = " << (int)d << endl; // 输出 -6
// 示例3:配合按位与将指定位置零
int flags = 0xFFFF;
flags = flags & ~(1 << 3); // 将第3位清零
cout << "flags after clear bit 3: " << hex << flags << endl; // 输出 fff7
// 示例4:配合按位与将多个位清零
unsigned char status = 0xFF; // 11111111
status = status & ~(0x03); // 将低2位清零,0x03取反为11111100
cout << "status after clear low 2 bits: " << hex << (int)status << endl; // 输出 fc
// 示例5:构造掩码
unsigned int mask = ~0; // 所有位全1
cout << "mask = " << hex << mask << endl;
return 0;
}Code language: PHP (php)
五、注意事项
- 按位取反 ~ 不要与逻辑非 ! 混淆。~ 是对每一位进行取反,! 是对整体布尔值取反(非0变为0,0变为1)。
- 取反操作的结果类型与操作数类型相同,如果操作数是较小类型(如 char),在执行运算时会被提升为 int,取反后再截断回原类型。
- 对有符号数取反时,结果通常以补码形式解释,可能得到负数。
- 在嵌入式编程和底层开发中,~ 常用于寄存器位的清零操作。
六、小结
- 按位取反(~)是单目运算符,对操作数的每一位取反。
- 常见用途:配合按位与将指定位置零、构造掩码。
- 注意区分 ~ 和 ! 的不同作用。
- 有符号数取反后可能变为负数,需注意类型提升和截断。