【问题标题】:Bitfield masks in CC中的位域掩码
【发布时间】:2009-10-10 21:35:21
【问题描述】:

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

理想情况下,我希望能够像这样自动清除字段:

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

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

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

【问题讨论】:

  • __sync_and_and_fetch 不适用于位域:“GCC 将允许长度为 1、2、4 或 8 字节的任何整数标量或指针类型。”

标签: c atomic bit-fields


【解决方案1】:

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

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;

【讨论】:

  • 请注意,typeof() 是 GCC 扩展,不能移植到其他编译器。
  • 出于好奇,如果它是不可移植的,为什么会被接受?只是好奇。
  • T那整行是一个 gcc 扩展,因为它是一个构造函数表达式。但是用 3 个作业替换它是微不足道的,每个字段一个。
  • typeof() 如果您被允许/愿意使用 C++ 功能,则将进入 C++0x
【解决方案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 失败时要处理的未显示代码)

【讨论】:

    【解决方案3】:

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

    【讨论】:

    • +1 我可以想象这是可行的。字节顺序和位域可能存在毛茸茸的问题,但我可以想象这是安全的。但我不是标准专家。
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

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

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

      【讨论】:

        猜你喜欢
        • 2012-07-21
        • 2019-11-19
        • 1970-01-01
        • 2010-09-23
        • 1970-01-01
        • 2023-03-07
        • 1970-01-01
        • 1970-01-01
        • 2021-11-27
        相关资源
        最近更新 更多