【问题标题】:Which end of a bit field is the most significant bit?位域的哪一端是最高有效位?
【发布时间】:2011-05-31 15:41:55
【问题描述】:

我正在使用 Visual Studio 2008 为 Windows XP/Vista/7 编写 C++ 应用程序。我的一些结构使用位域,如示例所示。

typedef struct myStruct_tag
{
    BYTE myVar1;
    WORD myVar2;
    WORD myVar3;
    union
    {
        struct
        {
            BYTE           :1;
            BYTE field1    :1;
            BYTE field2    :1;
            BYTE reserved  :5;
        } myBitField;
        BYTE myVar4;
    };
    BYTE myVar5;
    BYTE myVar6;
} myStruct_t;

字段的哪一端是最高有效位?

【问题讨论】:

  • 注:根据标准,MSB没有定义。在您的特定平台上,我怀疑 reserved 包含 MSB,但我不肯定。
  • @Billy:这看起来像是对我的回答。
  • 呃...这是一个相当奇怪的问题。您的声明中只有 3 位字段。其中 2 个是 1 位位域,即它们不存在“哪一端”的问题,因为那里只有 1 位。唯一的多位位域称为reserved,这表明它根本没有被使用。因此,基本上,您可以申请的唯一位域是reserved。你是在问reserved吗?如果不是,请澄清您的问题。
  • @Ben:如果我是积极的 w.r.t.哪个变量会得到 MSB 这本来是一个答案,但我不肯定......
  • 这很重要,因为该结构用于定义串行通信的数据包。位域需要在系统的两侧对齐,不幸的是我只能控制一侧。

标签: c windows visual-studio-2008 bit-fields


【解决方案1】:

C99 标准 6.7.2.1/10(强调我的):

一个实现可以分配任何大到足以容纳位域的可寻址存储单元。如果有足够的空间,结构中紧跟在另一个位域之后的位域将被打包到同一单元的相邻位中。如果剩余空间不足,则将不适合的位域放入下一个单元还是与相邻单元重叠是实现定义的。 单元内位域的分配顺序(高位到低位或低位到高位)由实现定义。 可寻址存储单元的对齐方式未指定。

因此,您的编译器实现必须记录该顺序。

但是,关于如何实现位域的很多内容是实现定义或未指定的,因此使用它们以可移植方式对硬件、线路协议或文件格式位域进行建模并不值得尝试。

如果您希望您的“位字段”对程序外部的某些内容(如上述内容)进行建模,请使用显式掩码,使用标准位运算符(|、'&@987654322)设置和清除位@~,

【讨论】:

  • 如果可以的话,+10。您对这个问题的回答是不将位域用于人们认为有用的主要事情的最佳理由。
  • 我没有意识到人们试图以可移植的方式使用它们,我看到的唯一用途是“从使用编译器 X 的程序与硬件对话”。并查看无数“我在编译器 X 中执行此操作,我如何在编译器 Y 中执行此操作”问题。
  • @CodeAbominator:例如,我见过用于破解网络协议消息或二进制文件格式的位域。我还看到一些代码在为不同的平台编译时无法工作。
  • 这正是位域的用途。如果未来的 C 标准定义了这种行为,那将很有用,但在嵌入式世界中,位域和联合是必要的,程序员和设备制造商在他们的头文件中使用它们。请注意,请参阅编译器文档,不要期望它是可移植的。在低级硬件依赖代码中使用,其中有必要指定特定位并且使用掩码会增加代码大小、降低性能并且没有任何好处(因为这种类型的代码如果根据定义不可移植)。
【解决方案2】:

Visual Studio 2008 编译器文档指出:

声明为位域的数据的顺序是从低位到高位

来自"C++ Bit Fields", MSDN C++ Language Reference, Visual Studio 2008 version

【讨论】:

  • 正如其他人所指出的,相关标准没有指定这一点,编写符合标准的代码很好,但这就是所提问题的答案,读者可以决定他们是否想用他们的力量做善事或令人敬畏。 ;P
【解决方案3】:

如果您要询问 myBitField 中的哪些位存储在内存中字节的哪些位中,那是 C 标准明确未定义的。你必须通过实验来学习。如果您正在做一些真正重要的事情,则可能值得使用一种方法,将 #define field1 作为十六进制值(例如,0x400x02)并将其放在您想要的位置.

【讨论】:

  • 不幸的是,这部分代码的功能测试至少需要几周甚至几个月的时间。 (要真正测试其中的任何内容,必须编写很多内容,因为我现在处于这样一个阶段,剩下的所有内容都或多或少地相互依赖于其他所有内容。)定义的常量是一种选择,但我更喜欢使用位字段,因为前者在我的代码中需要 很多 更多逻辑。
  • 好吧,一个快速的实验应该告诉你编译器现在在做什么......只需像上面那样定义联合,将 0 分配给 myVar4,然后将 1 分配给 field1 并打印结果值来自myVar4。不过,我会在field1 之前定义的未使用位上挂一个名字,以免编译器将其优化掉。
猜你喜欢
  • 1970-01-01
  • 2015-03-25
  • 2020-09-07
  • 1970-01-01
  • 1970-01-01
  • 2013-02-11
  • 1970-01-01
  • 1970-01-01
  • 2019-07-12
相关资源
最近更新 更多