我很难理解为什么在枚举中使用Bitwise操作 然后在代码中。 为什么不使用数字或布尔值 例如:
enum
{
RS_BLEND = (1 << 0),
RS_BLEND_FUNC = (1 << 1),
RS_CULL_FACE = (1 << 2),
RS_DEPTH_TEST = (1 << 3),
RS_DEPTH_WRITE = (1 << 4),
RS_DEPTH_FUNC = (1 << 5),
RS_CULL_FACE_SIDE = (1 << 6),
// RS_STENCIL_TEST = (1 << 7),
// RS_STENCIL_WRITE = (1 << 8),
// RS_STENCIL_FUNC = (1 << 9),
// RS_STENCIL_OP = (1 << 10),
RS_FRONT_FACE = (1 << 11),
RS_ALL_ONES = 0xFFFFFFFF,
};
void RenderState::StateBlock::setCullFace(bool enabled)
{
_cullFaceEnabled = enabled;
if (!enabled)
{
_bits &= ~RS_CULL_FACE;
}
else
{
_bits |= RS_CULL_FACE;
}
}
这不是关键或嵌入式软件。
答案 0 :(得分:4)
使用位而不是布尔值的优点是您可以直接操作值集。例如,定义:
const int FILLED = (1 << 0);
const int STROKED = (1 << 1);
const int SHADOW = (1 << 2);
const int BLINK = (1 << 3);
您可以使用接受draw_mode
参数的函数并将其称为
draw_symbol(FILLED | SHADOW | BLINK, "X");
即。直接传递值的子集。
使用容器而不是单个整数参数将需要更多代码来编写和读取。效率也会降低,但在某些情况下,这不是最重要的一点。
答案 1 :(得分:1)
这些枚举值是位域中使用的 flags (此处为_bits
)。通过声明布尔结构可以获得类似的行为:
struct field {
bool rs_blend;
bool rs_blend_func;
...
};
但是,这样的结构每个条目至少需要一个字节,并且难以处理,因此开发人员通过将这些值编码为整数值的位来采用不同的方法。
值为1的任何整数都设置了“最右侧”(最低有效)位,因此1 << i
恰好i
位等于1,这意味着该枚举中的每个常量(RS_BLEND
,RS_BLEND_FUNC
)编码一位。这实际上是一个相当普遍的习惯用法,特别是在C中,处理结构更加冗长。
通过使用按位运算,可以在此位域中立即设置或清除等。例如,RS_BLEND | RS_BLEND_FUNC
创建一个具有这两个标志集的位域。有关在此上下文中使用按位运算的详细信息,请参阅this主题。
答案 2 :(得分:0)
1位与8位。使用1/8内存是一个相当惊人的节省来代表完全相同的数据(并且使用8倍以上只是因为有些人对按位操作感到不舒服同样非常浪费),如果你经常处理热数据这可能很重要循环访问(我的i7上每个核心的L1缓存只有64KB,例如,更不用说有限的寄存器了)。
此外,如果在此处使用位,则可以测试使用单条指令设置的两个或更多位。您还可以使用FFZ / FFS一次有效地找到64位中的空闲位或设置位。您可以轻松地反转,例如,一次64位,而不是循环通过64个布尔值,并且必须同时执行该操作,这不仅对CPU非常有效,而且开发人员写出所有内容的效率和效率也较低那个代码,当他只能使用一个操作符时。
好处不断。我实际上认为更多的人应该使用按位操作,而不是更少。需要一些时间来习惯用比特来思考,但这不需要太长时间。
答案 3 :(得分:0)
很方便。
在示例代码中,显然(包括注释中的内容)至少有12个不同的设置,每个设置都打开或关闭,但可以组合打开或关闭。
比特摆弄允许打开或关闭各个设置组(包括一组),或者测试特定组是否开启或关闭。
数字值和数字操作可以做到这一点,但不是那么方便。
例如,如果使用
打开CULL_FACE和DEPTH_FACE设置中的至少一个,则更容易(并且不易出错) if (flags & (RS_CULL_FACE | RS_DEPTH_FACE))
跟踪这两个设置可能为真的所有可能数值,并对每个设置进行测试。