15

我来到了这行代码:

#define CPARSER_FLAGS_DEBUG        (1 << 0)

它有什么作用?它与以下内容相同:

#define CPARSER_FLAGS_DEBUG        (1)

对?

4

3 回答 3

17

是的。在设置标志值时,它可能用于对称性:

#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) { /* ... */ }
于 2013-02-26T17:25:20.813 回答
11

在受 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)

由于已知移位表达式的右侧,编译器将为每个值分别生成以下整数:

  • 1
  • 2
  • 4

起初这可能没有多大意义,但如果您以二进制表示形式查看这些值,则如下所示:

  • 0b001
  • 0b010
  • 0b100

换言之,每个值在不同位置仅设置一个位。然后,假设我们想要启用数据传输和魔术功能,但不启用我们想象中的设备的过滤。为此,我们只需要设置#0 和#2 位(1),以及取消设置#1 位(0)。这是当按位或运算符以及我们预定义的掩码派上用场的时候。我们要做的是:

uint32_t value_for_device = MYDEV_ENABLE_DATA_FLOW | MYDEV_ENABLE_MAGIC;

其中 "OR"0b0010b100,赋予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。您可以单独保留位偏移定义。

可能还有更多的理由不会立即出现在我的脑海中。

希望它能澄清一些事情。祝你好运!

于 2013-02-26T18:03:51.413 回答
10

通常这样做是为了表明定义代表一个位标志。特别是。当有多个标志一起定义时。在这种情况下,移位的幅度定义了定义所代表的位位置。像这样定义它也能让事情很好地排列起来:

#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;

然后使用位01(FOO & BAR) 调用函数并检查返回以查看位2(BAZ) 是否已设置。

于 2013-02-26T17:25:29.493 回答