C/C++ 中的嵌套位域
Nested bitfields in C/C++
我需要创建一个带有位域的结构来封装一些来自硬件的数据。假设我使用特定于编译器的机制来强制打包和排序,是否可以创建类似于以下(语法不正确)的结构:
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
需要从字节边界开始。我想知道是否有其他技巧可以实现位域的 "nesting"。
为了详细说明我之前的评论,这些内容应该允许您想要的访问方式。尽管以一种远非优雅的方式:
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 位域总是会导致代码效率低下且容易出现竞争。
在这种情况下你应该使用联合
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;
在 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 保留字段。 Ctor 可能应该将这些字段设置为 0 或 1,如协议指定的那样。
我需要创建一个带有位域的结构来封装一些来自硬件的数据。假设我使用特定于编译器的机制来强制打包和排序,是否可以创建类似于以下(语法不正确)的结构:
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
需要从字节边界开始。我想知道是否有其他技巧可以实现位域的 "nesting"。
为了详细说明我之前的评论,这些内容应该允许您想要的访问方式。尽管以一种远非优雅的方式:
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 位域总是会导致代码效率低下且容易出现竞争。
在这种情况下你应该使用联合
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;
在 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 保留字段。 Ctor 可能应该将这些字段设置为 0 或 1,如协议指定的那样。