旗帜,枚举(C)

qua*_*ano 14 c enums flags

我不习惯使用标志编程,但我认为我发现它们有用的情况:

我有几个对象将自己注册为某些事件的监听器.他们注册的事件取决于构造它们时发送给它们的变量.我认为一个很好的方法是发送按位OR连接变量,如:TAKES_DAMAGE | GRABBABLE | LIQUID等等.然后,在构造函数中,对象可以检查设置了哪些标志,并将其自身注册为侦听器.

但这是我感到困惑的地方.优选地,标志将在枚举中.但这也是一个问题.如果我们有这些标志:

enum
{
    TAKES_DAMAGE,/* (0) */
    GRABBABLE, /* (1) */
    LIQUID, /* (2) */
    SOME_OTHER /* (3) */
};
Run Code Online (Sandbox Code Playgroud)

然后发送标志SOME_OTHER(3)将与发送GRABBABLE |相同 LIQUID,不是吗?

你究竟如何处理这些东西?

Kea*_*eks 53

你的枚举需要是两个权力:

enum
{
    TAKES_DAMAGE = 1,
    GRABBABLE = 2,
    LIQUID = 4,
    SOME_OTHER = 8
};
Run Code Online (Sandbox Code Playgroud)

或者以更易读的方式:

enum
{
    TAKES_DAMAGE = 1 << 0,
    GRABBABLE = 1 << 1,
    LIQUID = 1 << 2,
    SOME_OTHER = 1 << 3
};
Run Code Online (Sandbox Code Playgroud)

为什么?因为您希望能够组合标志而不重叠,并且还能够执行此操作:

if(myVar & GRABBABLE)
{
    // grabbable code
}
Run Code Online (Sandbox Code Playgroud)

...如果枚举值如下所示,则有效:

 TAKES_DAMAGE: 00000001
 GRABBABLE:    00000010
 LIQUID:       00000100
 SOME_OTHER:   00001000
Run Code Online (Sandbox Code Playgroud)

所以,说你已经设置myVarGRABBABLE | TAKES_DAMAGE,这里的工作原理是,当你需要检查的GRABBABLE标志:

 myVar:     00000011
 GRABBABLE: 00000010 [AND]
 -------------------
            00000010 // non-zero => converts to true
Run Code Online (Sandbox Code Playgroud)

如果您设置myVarLIQUID | SOME_OTHER,则操作将导致:

 myVar:     00001100
 GRABBABLE: 00000010 [AND]
 -------------------
            00000000 // zero => converts to false
Run Code Online (Sandbox Code Playgroud)

  • 如果您不喜欢shift运算符方法,最好使用十六进制而不是十进制:TAKES_DAMAGE = 0x0001,GRABBABLE = 0x0002,LIQUID = 0x0004,GAS = 0x0008,PUSHABLE = 0x0010,INFLICTS_DAMAGE = 0x0020,OTHER = 0x0040等.在代码中对齐所有值时,它看起来很漂亮. (2认同)

Adr*_*son 28

另一种存储标志的方法是根本不打扰底层类型.当使用枚举时,枚举值默认存储在unsigned int中,在普通计算机上为32位.这只给你32个可能的标志:虽然很多,但有些情况下还不够.

现在你可以用这种方式定义你的标志:

typedef struct
{
    int takes_damage : 1;
    int grabbable    : 1;
    int liquid       : 1;
    int some_other   : 1;
} flags;
Run Code Online (Sandbox Code Playgroud)

如果您从未遇到过这种情况,则":1"部分告诉编译器仅使用1位来存储此结构成员.

现在你可以定义一个变量来保存标志,并使用这些标志:

flags myflags = {1,0,0,1}; // defines a variable holding a set of flags, with an initial value of takes_damage & some_other

myflags.liquid = 1; // change the flags to include the liquid

if ( myflags.takes_damage ) // test for one flag
    apply_damage();
if ( myflags.liquid && myflags.some_other ) // test for multiple flags
    show_strange_behavior();
Run Code Online (Sandbox Code Playgroud)

此方法允许您定义任意数量的标志,没有限制,您可以随时扩展您的标志集,而不必担心溢出.缺点是测试标志的子集更麻烦并且需要更多代码.

  • 有趣.我第一次遇到这种方法.有兴趣阅读比我更知识渊博的人的评论与两个枚举的传统力量相比的好处和缺点.它适用于所有编译器和所有架构吗?有什么陷阱? (3认同)

Fre*_*son 5

是.相反,让你的枚举成员2的权力:

enum
{
    TAKES_DAMAGE = (1 << 0),
    GRABBABLE = (1 << 1),
    LIQUID = (1 << 2),
    SOME_OTHER = (1 << 3)
};
Run Code Online (Sandbox Code Playgroud)