位域的最高位是哪一位?

16

我正在使用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;
哪一端是最高位?

3
注意:根据标准,MSB(最高有效位)没有定义。在您特定的平台上,我怀疑“保留”位包含MSB,但我不能确定。 - Billy ONeal
@Billy:在我看来,那似乎是一个答案。 - Ben Voigt
嗯...这是一个相当奇怪的问题。在你的声明中,只有3个位域。其中2个是1位位域,即它们没有“哪一端”的问题,因为那里只有1位。唯一的多位位域叫做“保留”,这表明它根本没有被使用。因此,基本上你所问的唯一位域是“保留”。你是在特别询问“保留”吗?如果不是,请澄清你的问题。 - AnT stands with Russia
这很重要,因为该结构用于定义串行通信的数据包。位域需要在系统的两侧对齐,而我只能控制其中一侧。 - Jim Fell
@Jim:嗯...试试一边,看看是否有效;如果不行,再试试另一边哈哈。 - Billy ONeal
显示剩余2条评论
3个回答

26
C99标准6.7.2.1/10(加粗部分为重点):
实现可以分配任何足够大以容纳位域的可寻址存储单元。如果剩余空间足够,紧跟在结构体中另一个位域后面的位域将被打包到同一单元的相邻位上。如果剩余空间不足,则未能适应的位域是放入下一单元还是与相邻单元重叠是由实现定义的。单元内位域的分配顺序(从高位向低位或从低位向高位)是由实现定义的。可寻址存储单元的对齐方式未指定。
因此,分配顺序必须由编译器实现进行文档化。
然而,有关位域的许多实现方式都是实现定义或未指定的,因此使用位域以便于在硬件、线协议或文件格式比特字段的模型中具有可移植性并不值得尝试。
如果您希望“位域”用于模拟程序外部的某些东西(如上述内容),请使用显式掩码,并使用标准位运算符(例如|,`&`, `~`, `<<`等)设置和清除位。在代码中使用帮助器内联函数(甚至是宏),以使其更轻松/清晰。

2
如果可以的话,我会给你加10分。你对这个问题的回答是不使用位域进行人们认为有用的主要事情的最好理由。 - R.. GitHub STOP HELPING ICE
1
我没有意识到人们试图在不同编译器之间移植它们,我所见过的唯一用途是“从我的使用编译器X的程序中与硬件通信”。看看所有那些无数的“我在编译器X中这样做,我该如何在编译器Y中实现”的问题。 - Code Abominator
@CodeAbominator:举个例子,我见过使用位域来破解网络协议消息或二进制文件格式的情况。但是,我也见过一些这样的代码在编译到不同平台时无法正常工作。 - Michael Burr
这正是位域的用途。如果未来的C标准定义了这种行为,那将会很有用。但在嵌入式世界中,位域和联合体是程序员和设备制造商在头文件中必需使用的。请注意,参考编译器文档,并不要期望它具有可移植性。在低级硬件相关代码中使用,需要指定特定的位,并且使用掩码会增加代码大小,降低性能并且没有任何好处(因为这种类型的代码本质上是不可移植的)。 - user1582568

8

正如其他人所指出的那样,相关标准并没有指定这一点,编写符合标准的代码很好,但这是对所提出问题的回答,读者可以决定他们想要将其用于善良还是卓越。;P - rakslice

2

如果你想知道myBitField中的哪些位存储在内存中的哪些位,这在C标准中是明确未定义的。你需要通过实验来学习。如果你正在做一些需要关注这个问题的事情,那么最好使用另一种方法,其中你可以将field1定义为十六进制值(例如0x400x02),并将其放在所需位置。


不幸的是,这部分代码的功能测试至少需要几周,甚至可能需要几个月的时间。(因为现在我所剩下的所有编写工作都或多或少地相互依存,所以必须编写大量内容才能真正测试其中任何一部分。)定义常量是一种选择,但我更喜欢使用位域,因为前者会在我的代码中需要更多的逻辑。 - Jim Fell
2
好的,一个快速的实验应该能向您展示编译器现在正在做什么...只需按上述定义联合,将0分配给myVar4,然后将1分配给field1并打印从myVar4得到的结果值。我会给你在field1之前定义的未使用的位取个名字,以防编译器优化它。 - Jay Maynard K5ZC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接