C 中是否有一种可移植的方式来在编译时找出位字段的掩码?
理想情况下,我希望能够以原子方式清除这样的字段:
struct Reference {
unsigned age : 3;
unsigned marked : 1;
unsigned references : 4;
};
struct Reference myRef;
__sync_and_and_fetch(&myRef, age, ~AGE_MASK);
否则我必须对结构进行锁定,这比我想要的要重。
C 中是否有一种可移植的方式来在编译时找出位字段的掩码?
理想情况下,我希望能够以原子方式清除这样的字段:
struct Reference {
unsigned age : 3;
unsigned marked : 1;
unsigned references : 4;
};
struct Reference myRef;
__sync_and_and_fetch(&myRef, age, ~AGE_MASK);
否则我必须对结构进行锁定,这比我想要的要重。
您可以执行以下操作:
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 失败时要处理的未显示代码)
我不知道如何在编译时执行此操作,但在运行时应该很简单,将位域结构的实例与适当大小的 unsigned int 联合起来,并将所有字段设置为 0,除了你的那个关心哪个应该设置为全 1 - unsigned int 的值就是你想要的位掩码。您可以在启动时为每个字段执行此操作,也许使用宏来避免一些重复的代码。可能还不够吗?
或者,如果你真的想要面具:
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;
我认为这是不可能的——即使使用 offsetof() 可以用于字节偏移,但似乎不适用于位域。我会将字段重新声明为枚举/定义(0x01 0x02 等)并自己管理这些位,因此您可以获得原子更改。
是的,这是可以做到的。您需要捕获该值并执行操作。然后,如果内存仍然包含旧值,则需要使用原子比较和交换(如 Windows 上的 InterlockedCompareExchange)来存储新值。如果有人修改了该值,则循环并重试。请注意,这是一种标准模式,用于对不可用内在函数的字大小的数据进行任何操作。
下面的代码使用 int - 正如 Keith 指出的那样,您可以使用联合来获取结构的值作为 int。
int oldValue, newValue;
do
{
oldValue = myRef;
newValue = oldValue & ~AGE_MASK;
} while (InterlockedCompareExchange(&myRef, newValue, oldValue) != oldValue);