在C中是否有可移植的方法在编译时找到位字段的掩码?
理想情况下,我希望能够原子地清除这样的字段:
struct Reference {
unsigned age : 3;
unsigned marked : 1;
unsigned references : 4;
};
struct Reference myRef;
__sync_and_and_fetch(&myRef, age, ~AGE_MASK);
否则我必须取消对结构的锁定,这比我想要的重量级更重。
答案 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);