定义SOMETHING(1 << 0)
我来accros这行代码:
#define CPARSER_FLAGS_DEBUG (1 << 0)
它有什么作用? 它与以下相同:
#define CPARSER_FLAGS_DEBUG (1)
对?
在C启发式语言中, <<
和>>
运算符是左右位运算符(尽管在C ++中它们可以被重载 - 最着名的重载可能是I / O流运算符)。 例如,
x = y << 2;
指定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)
由于shift表达式的右边是已知的,因此编译器将分别为每个值生成以下整数:
起初可能没有多大意义,但如果以二进制表示查看这些值,则看起来像这样:
换句话说,每个值只有一个位设置在不同的位置。 然后,假设我们想要启用数据传输和魔术功能,但不启用我们假想设备的过滤功能。 为此,我们必须仅将位#0和#2设置为( 1
),并将位#1设置为( 0
)。 这是当我们的预定义的掩码方便的时候,按位或运算符。 我们做的是这样的:
uint32_t value_for_device = MYDEV_ENABLE_DATA_FLOW | MYDEV_ENABLE_MAGIC;
其中“OR”s 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 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) { /* ... */ }
这通常是为了显示定义代表一个位标志。 ESP。 当有多个标志一起定义时。 在这种情况下,移位的大小定义了定义所代表的位的位置。 像这样定义它也使事情很好地排列:
#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)。
上一篇: define SOMETHING (1 << 0)
下一篇: DRYing up JavaScript functions taking optional arguments and a callback