C/C++中的嵌套位域

6
我需要创建一个包含位域的结构体,以封装一些来自硬件的数据。假设我使用编译器特定的机制来强制打包和排序,那么是否可以创建类似以下结构体(不是语法上正确的):
typedef struct _BYTE_OF_DATA
{
    uint8_t Reserved1 : 2;
    struct
    {
        uint8_t BitWithSomeMeaning : 1;
        uint8_t BitWithSomeOtherMeaning : 1;
    } BitsWithMeaning;
    uint8_t Reserved2 : 4;
} BYTE_OF_DATA, *PBYTE_OF_DATA;

static_assert(sizeof(BYTE_OF_DATA) == 1, "Incorrect size");

然后可以按照以下方式进行访问:

BYTE_OF_DATA byteOfData;

byteOfData.Reserved1 = 1;
byteOfData.BitsWithMeaning.BitWithSomeOtherMeaning = 0;

我上面描述的确切方案行不通,因为我猜测结构体BitsWithMeaning需要从字节边界开始。我想知道是否有其他技巧可以实现位域的“嵌套”。


3
允许使用宏吗?这是C++还是C语言?我认为是C++,因为我不认为C语言具有static_assert - Mooing Duck
由于存在static_assert,我猜测这应该是C++,因此应该删除C标签。 - Paul R
3
自C11起,C语言引入了static_assert。该问题也可能与C语言相关。 - Morwenn
@Morwenn:啊,谢谢,我不知道这个(我还在使用C99)。 - Paul R
1
我认为没有绕过这个问题的方法。我多年前遇到了这个问题,因此现在我总是手动进行位操作而不是使用位域(:<>)。 - thang
显示剩余4条评论
3个回答

7
为了进一步说明我的上一个评论,以下内容应该可以实现您想要的访问方式。尽管这种方法远非优雅:
typedef union _BYTE_OF_DATA {
    struct {
        uint8_t Reserved1 : 2;
        uint8_t : 2;
        uint8_t Reserved2 : 4;
    };
    struct {
        uint8_t : 2;
        uint8_t BitWithSomeMeaning : 1;
        uint8_t BitWithSomeOtherMeaning : 1;
        uint8_t : 4;
    } BitsWithMeaning;
} BYTE_OF_DATA, *PBYTE_OF_DATA;

个人而言,我更喜欢传统的字段掩码和位置常量,并手动操作寄存器。我的经验是,以这种方式访问易失性I/O位域通常会导致效率低下且容易出现竞争的代码。


点赞,因为它确实实现了我想要执行的访问类型。但是,正如您提到的那样,这确实使结构本身变得难以阅读;不太明显该寄存器的结构是什么样的。 - TripShock
_BYTE_OF_DATA 标识符被保留用于实现(无论是在 C 还是 C++ 中)。在 C++ 中,您甚至不需要使用 typedef - MSalters
@MSalters:这个命名是从原始问题中继承下来的,而且很有可能这就是实现方式。这些类型的I/O寄存器定义通常作为嵌入式C/C++编译器的设备头文件的一部分被遇到。 - doynax

1
在这种情况下,您应该使用联合(union)。
typedef union _BYTE_OF_DATA {
    uint8_t data;
    struct {
        uint8_t padding1 : 2;
        uint8_t BitWithSomeMeaning : 1;
        uint8_t BitWithSomeOtherMeaning : 1;
        uint8_t padding 2 : 4;
    } BitsWithMeaning;
} BYTE_OF_DATA, *PBYTE_OF_DATA;

static_assert(sizeof(BYTE_OF_DATA) == 1, "Incorrect size");

所以您可以一次性填写数据:
BYTE_OF_DATA myByte;

myByte.data = someotherbyte;

并且理解其中的含义:

int meaning1 = myByte.BitWithSomeMeaning;
int meaning2 = myByte.BitWithSomeOtherMeaning;

或者相反操作:

myByte.data = 0; // Put all fields to 0

myByte.BitWithSomeMeaning = 1;
myByte.BitWithSomeOtherMeaning = 0;

int data = myByte.data;

0
在C++中,最简单的解决方案可能是
struct BYTE_OF_DATA
{
    uint8_t bits;
    private:
    struct
    {
        bool getBitWithSomeMeaning() const { return bool(bits&0x20); }
        bool getWithSomeOtherMeaning() const { return bool(bits&0x10); }
        void setBitWithSomeMeaning(bool b);
        void setWithSomeOtherMeaning(bool b);
    } BitsWithMeaning;
};

显然为保留字段不提供getters/setters。构造函数应该将这些字段设置为0或1,正如协议所规定的那样。


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