C中的位域掩码

时间:2009-10-10 21:35:22

标签: c atomic bit-fields

在C中是否有可移植的方法在编译时找到位字段的掩码?

理想情况下,我希望能够原子地清除这样的字段:

struct Reference {
    unsigned age : 3;
    unsigned marked : 1;
    unsigned references : 4;
};

struct Reference myRef;
__sync_and_and_fetch(&myRef, age, ~AGE_MASK);

否则我必须取消对结构的锁定,这比我想要的重量级更重。

5 个答案:

答案 0 :(得分:2)

您可以执行以下操作:

union Reference {
  unsigned asWord;
  struct {
    unsigned age : 3;
    unsigned marked : 1;
    unsigned references : 4;
  } asFields;
}

要原子清除myRef的字段,请执行

union Reference myRef;

union Reference oldr = myRef;
union Reference newr = oldr;
newr.asFields.age = 0;
compare_and_swap(&myRef.asWord, oldr.asWord, newr.asWord);

(当compare_and_swap失败时,+未示出的代码将被处理)

答案 1 :(得分:2)

我不知道如何在编译时这样做,但是在运行时它应该是一个简单的问题,即使用适当大小的unsigned int将bitfield结构的实例联合起来,并将所有字段设置为0除外你关心的那个应该设置为全1 - - unsigned int的值是你想要的位掩码。您可以在启动时为每个字段执行此操作,也可以使用宏来避免某些重复代码。可能不够吗?

答案 2 :(得分:2)

或者,如果你真的想要面具:

union Reference {
  unsigned asWord;
  struct {
    unsigned age : 3;
    unsigned marked : 1;
    unsigned references : 4;
  } asFields;
}

Reference agemask_ref;
agemask_ref.asFields = (typeof(agemask_ref.asFields)){0, -1, -1};
unsigned agemask = agemask_ref.asWord;

答案 3 :(得分:1)

我认为不可能 - 即使offsetof()适用于字节偏移,但似乎不适用于位域。我会将字段重新声明为枚举/定义(0x01 0x02等)并自行管理这些位,这样您就可以进行原子更改。

答案 4 :(得分:0)

是的,这可以做到。您需要捕获值并执行操作。然后,如果内存仍包含旧值,则需要使用原子比较和交换(如Windows上的InterlockedCompareExchange)来存储新值。如果有人修改了该值,则循环并重试。请注意,这是一个标准模式,用于对没有内在函数的字大小的数据进行任何操作。

下面的代码使用了一个int - 正如Keith所指出的,你可以使用union来获取结构的值作为int。

int oldValue, newValue;
do
{
    oldValue = myRef;
    newValue = oldValue & ~AGE_MASK;
} while (InterlockedCompareExchange(&myRef, newValue, oldValue) != oldValue);