如何检查在C位域中设置的单个字段

时间:2015-05-13 22:16:11

标签: c

我有以下样式的联合 - 在界面中定义,因此不容易更改。

我想检查foo是否是唯一被设置的字段。并且不希望通过逐项列出所有其他字段。

所以我的直接想法是构建一个掩码,但是bitfield正在做它最好隐藏详细信息,比如命名字段的位置。

我想不出比用一个字段集创建变量然后反转原始字段更好的事情。有更整洁的解决方案吗?

typedef union    struct {
        unsigned char user:1;
        unsigned char zero:1;
        unsigned char foo:1;
        unsigned char bar:1;
        unsigned char blah:1;
        unsigned char unused:3;
    };
    unsigned char raw;
} flags_t;

2 个答案:

答案 0 :(得分:2)

按位异或,否定你想要的东西:

  11011111
^ 00100000
= 11111111

然后检查值== 255.通过使用自己的结构来构建否定,可以通过将bar-> foo = 0和其他所有内容设置为1来使其清除。

编辑:稍微详细一点,因为当你要求的时候,我觉得自己很不舒服:

struct {
    unsigned char bad:1;
    unsigned char bad:1;

    unsigned char foo;

    unsigned char other:1;
    unsigned char bad:1;
    unsigned char things:3;
} state_checker;

int some_checking_function(flags_t possible_foo) {
    result = possible_foo ^ state_checker;
    result = !(result - 255u);
    return result;
}

理想情况下,你甚至会创建一个使用与你正在检查的值相同的结构的常量,以确保在编译时没有任何不可思议的事情,但这是基本的想法。

答案 1 :(得分:0)

位域中每个位的确切位置取决于实现。但是编译器会做一致的事情,所以一旦你确定你关心的位在你的union / struct中,你可以构造一个掩码。

正如您所推断的,您可以为位域中的任何命名位构造一个掩码,并使用set / clear / toggle / check操作对其进行操作。 This question shows how为单个位(在C中)执行上述操作。下面提供的函数显示了如何使用并集的原始部分访问set,clear和toggle(假设原始部分的大小与分配的位域部分相同。

如何检查/访问联盟中的foo,

fooget( flags_t flag )
{
flags_t mask;
mask.raw = 0;
mask.foo = 1;
return(foo = flag.raw & mask.foo);
}

如何使用按位运算符和掩码

清除foo
fooclear( flags_t flag )
{
flags_t mask;
mask.raw = 0;
mask.foo = 1;
return( flag.raw &= ~mask.raw );
}

如何使用raw和mask

上的按位运算符设置foo
fooset( flags_t flag )
{
flags_t mask;
mask.raw = 0;
mask.foo = 1;
flag.raw |= mask.raw;
}

如何使用带掩码的raw运算符来切换(反转/翻转)foo,

footoggle( flags_t flag )
{
flags_t mask;
mask.raw = 0;
mask.foo = 1;
flag.raw ^= mask.raw;
}

警告:这与bitfields的精神背道而驰,后者隐藏了位操作的细节,使这些位操作“更容易”。