5

C 中是否有一种可移植的方式来在编译时找出位字段的掩码?

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

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

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

否则我必须对结构进行锁定,这比我想要的要重。

4

5 回答 5

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 失败时要处理的未显示代码)

于 2009-10-10T21:55:58.190 回答
2

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

于 2009-10-10T21:56:18.203 回答
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;
于 2009-10-10T22:05:31.783 回答
1

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

于 2009-10-10T21:54:44.757 回答
0

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

下面的代码使用 int - 正如 Keith 指出的那样,您可以使用联合来获取结构的值作为 int。

int oldValue, newValue;
do
{
    oldValue = myRef;
    newValue = oldValue & ~AGE_MASK;
} while (InterlockedCompareExchange(&myRef, newValue, oldValue) != oldValue);
于 2009-10-10T22:21:02.807 回答