在受 C 启发的语言中,<< 和 >> 运算符是左右 bitwise shift 运算符(尽管在 C++ 中它们可以被重载——最著名的重载可能是 I/O stream operators)。例如,
x = y << 2;
将 y 左移两位的结果赋值给 x。
通常你会在低级代码中看到很多字节移位,这就是为什么......任何硬件都提供了一种配置和控制其行为的方法,但没有人愿意使用整个整数来表示,比如说,ON/关闭状态。因此,硬件开发人员通常提供单个整数(也称为寄存器,通常是无符号 32 位)并声明,例如,位 #0 启用或禁用数据传输,位 #1 启用或禁用数据过滤,位 #3 确实一些其他的魔法等等力量。通常,可以同时读取或更改一项或多项设置。现在想象一下这对软件开发人员来说是多么方便——程序员不必处理简单的整数(或布尔值),而是必须处理 CPU 通常无法寻址的位。为了简化他们的生活,开发人员定义了掩码。继续上面的例子,配置掩码可能如下所示:
#define MYDEV_ENABLE_DATA_FLOW (1u<<0)
#define MYDEV_ENABLE_FILTERING (1u<<1)
#define MYDEV_ENABLE_MAGIC (1u<<2)
由于已知移位表达式的右侧,编译器将分别为每个值生成以下整数:
起初可能没有多大意义,但如果您以二进制表示形式查看这些值,则看起来像这样:
换句话说,每个值在不同的位置只有一个位。然后,假设我们想要启用数据传输和魔术功能,但不启用我们想象中的设备的过滤。为此,我们只需要设置#0 和#2 位(1),并取消设置#1 位(0)。这是 Bitwise OR 运算符以及我们预定义的掩码派上用场的时候。我们要做的是:
uint32_t value_for_device = MYDEV_ENABLE_DATA_FLOW | MYDEV_ENABLE_MAGIC;
0b001 和 0b100 的“或”,给出 0b101 值。我们将其发送到设备,它会检查每一位并启用或禁用相应的功能。
也经常使用其他位操作。比如说,我们不知道当前启用或禁用了什么,我们不想更改任何内容,只需确保关闭数据过滤即可。这可以通过读取当前配置、取消设置位并将其写回来完成。例如:
uint32_t value;
value = read_from_device();
value &= (~MYDEV_ENABLE_FILTERING);
write_to_device(value);
当然,这不是唯一的用法。很多有用的例子,见"Bit Twiddling Hacks" by Sean Eron Anderson。
好的,回到你最初的问题——为什么写(1<<0) 而不仅仅是(1)?原因有很多:
一致性
有这样的东西更一致:
#define A (1<<0)
#define B (1<<1)
#define C (1<<2)
而不是这样:
#define A (1)
#define B (1<<1)
#define C (1<<2)
甚至这个:
#define A 1
#define B 2
#define C 4
可维护性
容易改变周围的东西
它使改变周围的事物变得更容易。通过仅更改移位宽度而不是继续添加/删除 '
意图表达
它向阅读代码的人清楚地说明了作者的意图。只是1 意义不大。但是当您看到1<<0 时,您很可能会认为涉及到位移并且代码使用位掩码。
灵活性
想象一下,应该执行移位的数字被定义为一个宏。例如:
#define MY_BIT (1u<<MAGIC_BIT_OFFSET)
那么,你真的不知道结果是不是1。并且您可以单独保留位偏移定义。
我可能没有立即想到这样做的更多理由。
希望它能解决一些问题位。祝你好运!