定义SOMETHING(1 << 0)

Vet*_*ras 12 c

我来了accros这行代码:

#define CPARSER_FLAGS_DEBUG        (1 << 0)
Run Code Online (Sandbox Code Playgroud)

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

#define CPARSER_FLAGS_DEBUG        (1)
Run Code Online (Sandbox Code Playgroud)

对?

md5*_*md5 16

是的.也许它在设置标志值时用于对称:

#define FLAG_1  (1 << 0)
#define FLAG_2  (1 << 2)
#define FLAG_3  (1 << 3)
/* ... */
Run Code Online (Sandbox Code Playgroud)

不要担心性能,好的编译器就能优化这样的操作.

您可以将这些值组合如下:

/* Flags FLAG_1, FLAG_2 and FLAG_3 are set. */
f = FLAG_1 | FLAG_2 | FLAG_3;
Run Code Online (Sandbox Code Playgroud)

并测试是否设置了给定标志:

/* True if FLAG_1 is set. */
if (f & FLAG_1) { /* ... */ }
Run Code Online (Sandbox Code Playgroud)

  • 我认为即使是*糟糕的*编译器也可以优化它. (7认同)

Wil*_*ill 10

通常这样做是为了表明定义代表一个标志.ESP.当有多个标志一起定义时.在这种情况下,移位的大小定义了定义所代表的位位置.这样定义也可以很好地排列:

#define FOO_FLAG (1 << 0)
#define BAR_FLAG (1 << 1)
#define BAZ_FLAG (1 << 2)
Run Code Online (Sandbox Code Playgroud)

这可以在调用需要一系列标志的函数时使用:

int ret = some_function(FOO_FLAG | BAR_FLAG) & BAZ_FLAG;
Run Code Online (Sandbox Code Playgroud)

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


小智 9

在C-启发的语言,<<>>运营商都左,右按位移位运算符(虽然在C++中,他们可以被重载-最有名的超载可能是I/O流运营商).例如,

x = y << 2;
Run Code Online (Sandbox Code Playgroud)

分配x将y向左移位两位的结果.

通常你会在低级代码中看到很多字节移位,这就是为什么......任何硬件提供了一种配置和控制其行为的方法,但是没有人想用整个整数来表示ON/OFF状态.因此,硬件开发人员通常提供单个整数(也称为寄存器,通常是无符号32位),并指出例如位#0启用或禁用数据传输,位#1启用或禁用数据过滤,位#3执行一些其他的魔法,所以一个和所以力量.通常,可以同时读取或更改一个或多个设置.现在想象一下软件开发人员有多方便 - 程序员不必使用简单的整数(或布尔值),而是处理通常无法通过CPU寻址的位.为了简化生活,开发人员定义了掩码.坚持使用上面的示例,配置掩码可能如下所示:

#define MYDEV_ENABLE_DATA_FLOW (1u<<0)
#define MYDEV_ENABLE_FILTERING (1u<<1)
#define MYDEV_ENABLE_MAGIC     (1u<<2)
Run Code Online (Sandbox Code Playgroud)

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

  • 1
  • 2
  • 4

最初可能没有多大意义,但如果你在二进制表示中查看这些值,看起来像这样:

  • 项目0B001
  • 0b010
  • 0b100时

换句话说,每个值只有一个位设置在不同的位置.然后,假设我们想要启用数据传输和魔术功能,但不启用对我们想象设备的过滤.为此,我们必须只有#0和#2 1位()和#1位未设置(0).这是Bitwise OR运算符和我们预定义的掩码一起使用的时候.我们做的是这样的:

uint32_t value_for_device = MYDEV_ENABLE_DATA_FLOW | MYDEV_ENABLE_MAGIC;
Run Code Online (Sandbox Code Playgroud)

哪个"或" 0b0010b100,给予0b101价值.我们将其发送到设备,它检查每一位并启用或禁用相应的功能.

通常也使用其他位操作.比如说,我们不知道当前启用或禁用了什么,我们不想更改任何内容,只需确保数据过滤已关闭.这可以通过读取当前配置,取消设置位并将其写回来完成.例如:

uint32_t value;
value = read_from_device();
value &= (~MYDEV_ENABLE_FILTERING);
write_to_device(value);
Run Code Online (Sandbox Code Playgroud)

当然,这不是唯一的用法.有很多有用的例子,请参阅Sean Eron Anderson的"Bit Twiddling Hacks".

好的,回到原来的问题 - 为什么要写(1<<0)而不是简单(1)?原因有很多:

一致性

有这样的事情更加一致:

#define A (1<<0)
#define B (1<<1)
#define C (1<<2)
Run Code Online (Sandbox Code Playgroud)

而不是这个:

#define A (1)
#define B (1<<1)
#define C (1<<2)
Run Code Online (Sandbox Code Playgroud)

甚至这个:

#define A 1
#define B 2
#define C 4
Run Code Online (Sandbox Code Playgroud)

可维护性

容易改变周围的事物

它可以更容易地改变现状.只更改移位宽度而不是继续添加/删除'<更容易更改周围的事物

表达意图

它明确说明了作者阅读代码的意图.只是1意义不大.但是当你看到1<<0你时,你很可能会认为涉及到位移,并且代码可以使用位掩码.

灵活性

想象一下,应该执行移位的数量被定义为宏.例如:

#define MY_BIT (1u<<MAGIC_BIT_OFFSET)
Run Code Online (Sandbox Code Playgroud)

然后,你真的不知道结果是否合适1.您可以单独保留位偏移定义.

可能有更多的理由要做到这一点,我不会马上想到这一点.

希望它能稍微清理一下.祝好运!

  • +1很好的答案.但是`(iu << 0)`==`(i << 0)`其中为(-1u >> 0)!=( - 1 >> 0)` (2认同)