我来到了这行代码:
#define CPARSER_FLAGS_DEBUG (1 << 0)
它有什么作用?它与以下内容相同:
#define CPARSER_FLAGS_DEBUG (1)
对?
我来到了这行代码:
#define CPARSER_FLAGS_DEBUG (1 << 0)
它有什么作用?它与以下内容相同:
#define CPARSER_FLAGS_DEBUG (1)
对?
是的。在设置标志值时,它可能用于对称性:
#define FLAG_1 (1 << 0)
#define FLAG_2 (1 << 2)
#define FLAG_3 (1 << 3)
/* ... */
不要担心性能,一个好的编译器将能够优化这样的操作。
您可以按如下方式组合这些值:
/* Flags FLAG_1, FLAG_2 and FLAG_3 are set. */
f = FLAG_1 | FLAG_2 | FLAG_3;
并测试是否设置了给定标志:
/* True if FLAG_1 is set. */
if (f & FLAG_1) { /* ... */ }
在受 C 启发的语言中,<<
运算>>
符是左移和右移运算符(尽管在 C++ 中它们可以重载——最著名的重载可能是I/O 流运算符)。例如,
x = y << 2;
将 y 左移两位的结果赋给 x.
通常你会在低级代码中看到很多字节移位,这就是为什么......任何硬件都提供了一种配置和控制其行为的方法,但没有人愿意使用整个整数来表示,比如说,开/关状态。因此,硬件开发人员通常提供单个整数(也称为寄存器,通常是无符号 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
)。这是当按位或运算符以及我们预定义的掩码派上用场的时候。我们要做的是:
uint32_t value_for_device = MYDEV_ENABLE_DATA_FLOW | MYDEV_ENABLE_MAGIC;
其中 "OR"0b001
和0b100
,赋予0b101
价值。我们将其发送到设备,它会检查每一位并启用或禁用相应的功能。
也经常使用其他位操作。比如说,我们不知道当前启用或禁用了什么,我们不想更改任何内容,只需确保关闭数据过滤即可。这可以通过读取当前配置、取消设置位并将其写回来完成。例如:
uint32_t value;
value = read_from_device();
value &= (~MYDEV_ENABLE_FILTERING);
write_to_device(value);
当然,这不是唯一的用法。有关许多有用的示例,请参阅Sean Eron Anderson 的“Bit Twiddling Hacks”。
好的,回到你最初的问题——为什么要写作(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
。您可以单独保留位偏移定义。
可能还有更多的理由不会立即出现在我的脑海中。
希望它能澄清一些事情。祝你好运!
通常这样做是为了表明定义代表一个位标志。特别是。当有多个标志一起定义时。在这种情况下,移位的幅度定义了定义所代表的位位置。像这样定义它也能让事情很好地排列起来:
#define FOO_FLAG (1 << 0)
#define BAR_FLAG (1 << 1)
#define BAZ_FLAG (1 << 2)
这可以在调用需要一系列标志的函数时使用,如下所示:
int ret = some_function(FOO_FLAG | BAR_FLAG) & BAZ_FLAG;
然后使用位0
和1
(FOO & BAR) 调用函数并检查返回以查看位2
(BAZ) 是否已设置。