【问题标题】:OS independent programming of bitfields独立于操作系统的位域编程
【发布时间】:2012-08-25 00:23:32
【问题描述】:

我必须在跨越多个 DWORD 的位域中编写独立位。我目前使用的结构如下

typedef struct _myStruct
{
    union 
    {
        struct 
        {
            DWORD   field1       : 16
            DWORD   field2       : 8
            DWORD   field3       : 8
        };
        DWORD   value0;
    };
    union 
    {
        struct 
        {
            DWORD   field4       : 32; 
        } 
        DWORD   value1;
    };
} myStruct;

我这样做是为了让程序员可以直接访问独立的字段,而不是记住相应的 DWORD,即 myStruct.field1 等。 这在 Visual Studio 中运行良好,但是当我使用未命名的结构和联合时 GCC 会抱怨。为了纠正这一点,我尝试如下命名联合和结构:

    union _DW0
    {
        struct _BF
        {
            DWORD   field1       : 16
            DWORD   field2       : 8
            DWORD   field3       : 8
        } BF;
        DWORD   value0;
    } DW0;

但是现在访问对程序员不友好.. 即尝试对此进行编程的人必须记住每个字段属于哪个 DWORD。例如:myStruct.DW0.field1

有没有办法解决这个问题?

【问题讨论】:

  • TYPO: myStruct.DW0.field1 在最后一行被读取为 myStruct.DW0.BF.field1
  • 您的问题似乎更多是关于对匿名结构支持而不是位域的支持。但是,您应该记住,如果您希望位域在编译器和平台之间具有“二进制可移植性”(即位域的布局匹配),那可能有点雷区。
  • 也许值得解释一下为什么你不能使用 std::bitset 或 boost::dynamic_bitset
  • @Dave:在 gcc 4.3.4 和更新版本中运行良好独立于操作系统是完全不同的东西。

标签: c++ visual-studio gcc bit-fields


【解决方案1】:

位域本质上是不可移植的。当您编写DWORD field1 : 16; 时,标准不会确定field1 应该具有结构的高16 位还是低16 位。另一方面,如果您使用正确的类型和联合(在您的情况下就足够了,因为您的所有位域都与大多数平台中的类型匹配),那么 可以 是可移植的。

使用 C++11 类型(您也可以使用具有适合您平台的类型的库):

union {
   struct {
      uint16_t  _1;
      uint8_t   _2;
      uint8_t   _3;
   } field;
   uint32_t value;
};
// ...

【讨论】:

  • 你为什么不使用未命名的结构?
  • @Dave:那一个未命名的结构,以及一个同名的变量。
  • 我正在为一个平台编程,所以我不关心跨平台的可移植性或字节序。但是,我确实关心跨操作系统的可移植性。我有 typedef-ed DWORD 在所有操作系统中都是无符号的 32 位 int。另外,我的位域只是一个示例。位域甚至可能是 16、8 以外的值。所以我不能像上面描述的那样使用 uint16_t 和 uint8_t。
  • @Deepak:这不是字节顺序的问题,在同一平台甚至操作系统中,两个编译器可能会决定以不同的方式表示位域。位域的规范非常非常宽松。换一种说法:union U { struct A { unsigned char a : 4; unsigned char b : 4; } a; char u}; A value; value.a.a = 15; 如果您以十六进制打印value.u,它将是0x0F0xF0,并且标准没有要求它是哪一个。由于这个原因,位域非常不便携。
【解决方案2】:

匿名联合通常是可以的,但结构需要有一个名称。您也许可以使用它从语法中删除一级“点”。

但是,如果您需要通过命名结构成员单独访问字段(作为位域)作为一个整体(作为 DWORDS)访问字段,那么您将不得不使用稍微混乱的语法。这种用例需要联合,这需要内部结构,这会导致问题。您总是可以将所有杂乱的语法隐藏在 getter/setter 函数后面,但如果结构很大,那么该方法将无法很好地维护。

您可以考虑以下替代方案:

typedef struct
{
    // DWORD 0
    DWORD   field1   : 16;
    DWORD   field2   : 8;
    DWORD   field3   : 8;
    // DWORD 1
    DWORD   field4   : 32; 
} bitfield_struct;

这只会为您提供位域级别的访问权限,但语法更简洁。但是,如果需要,您仍然可以访问 DWORD 级别的数据:

bitfield_struct foo;
DWORD* bar = (DWORD*)&foo;

bar[0] = ...;
bar[1] = ...;

如果您通常通过位域访问数据而很少通过 DWORD 访问数据,那么这可能是一个可以接受的解决方案。

【讨论】:

  • 请注意,该标准不对位域的布局提供任何保证(嗯,至少不多)。特别是,位域的第一个字段是使用底层类型的低位还是最高位取决于平台。
  • 甚至比平台相关更具体,它是由实现定义的。针对同一平台的两个编译器可以使用不同的位域分配布局。
  • David, Michael- 好点,我打算将其包含在我的原始帖子中。位域在实现上留下了太多的东西,以至于它们很难便携使用。唯一真正可移植的方法是显式指定每个位和字节(即,将结构视为字节数组,使用索引访问字节,并使用位掩码访问位)。
猜你喜欢
  • 2013-06-14
  • 1970-01-01
  • 1970-01-01
  • 2010-12-22
  • 2010-09-14
  • 1970-01-01
  • 2012-06-17
  • 2013-04-24
  • 1970-01-01
相关资源
最近更新 更多