C++ 为什么这里压缩结构的大小是5而不是4字节?

C++ 为什么这里压缩结构的大小是5而不是4字节?,c++,c,gcc,struct,bit-fields,C++,C,Gcc,Struct,Bit Fields,参见在线示例: 为什么编译器在这里报告结构的大小为5字节而不是4字节?它应该包含32位。 这是因为内存对齐:编译器不会在字节的中间开始 CuFLAGG/,它将在下一个字节的开头(可能是*)开始。因此,您有四个字节用于初始联合,一个字节用于canFlags 例如,如果您将canFlags移动到union中,它(可能*)的大小将为4: typedef struct structTag { union { struct { uint32_t messageID : 26; /

参见在线示例:


为什么编译器在这里报告结构的大小为5字节而不是4字节?它应该包含32位。

这是因为内存对齐:编译器不会在字节的中间开始<代码> CuFLAGG/<代码>,它将在下一个字节的开头(可能是*)开始。因此,您有四个字节用于初始联合,一个字节用于
canFlags

例如,如果您将
canFlags
移动到union中,它(可能*)的大小将为4:

typedef struct structTag {
  union {
    struct {
      uint32_t messageID : 26; /* 26bit message id, 67108864 ids */
      uint8_t priority : 3; /* priority: MUST BE 0 */
    } __attribute__ ((packed));
    uint32_t rawID : 29;
    uint8_t canFlags : 3; /* <==== Moved */
  } __attribute__ ((packed));
} __attribute__ ((packed)) idSpecial;
typedef结构标签{
联合{
结构{
uint32_t消息id:26;/*26位消息id,67108864个id*/
uint8\u t优先级:3;/*优先级:必须为0*/
}_uuu属性_uuu((压缩));
uint32_t rawID:29;
uint8\u t canFlags:3;/*在某些编译器中,要“合并”位,所有项必须是相同的类型。因此,将其设置为
uint32\u t
,您现在有
uint8\u t
-在编译器IdeOne使用的那些

[无论如何,如何合并位仍取决于编译器,因此绝对保证数据存储为32位的唯一方法是使用单个
uint32_t
并声明一个类,该类执行相关的移位和anding/oring操作值-唯一保证是结构中的一个元素将至少具有您要求的位数]

正如其他人所指出的,除了字节边界之外,不能启动新结构。我通过在联合体中使用第二个结构来修复它,如下所示:

#包括
#包括
类型定义结构标签{
联合{
结构{
uint32_t消息id:26;/*26位消息id,67108864个id*/
uint8\u t优先级:3;/*优先级:必须为0*/
}_uuu属性_uuu((压缩));
结构{
uint32_t rawID:29;
uint8_t canFlags:3;
};
}_uuu属性_uuu((压缩));
}_uuu属性uuuu((打包))i特殊;
int main(){
printf(“大小:%d”,大小f(idSpecial));
返回0;
}

问题在于
\uuuuu属性\uuuuu((打包))
不执行按位打包。它只保证
结构成员之间没有填充。您可以尝试这个更简单的示例,其中大小也报告为5:

typedef struct structTag {
    struct {
      uint32_t messageID : 26;
      uint8_t priority : 3;
    } __attribute__ ((packed));
    uint8_t canFlags : 3;
} __attribute__ ((packed)) idSpecial;

按位打包仅适用于位域成员。您需要重新设计结构,使其成为具有位域messageID/priority/canFlags的结构和具有位域rowID/canFlags的结构的并集。换句话说,您需要进行一些复制或使用访问器宏或成员函数。

数据被安排并使用数据结构对齐在计算机内存中访问。这有两个相关问题

  • 对齐
  • 填充物
  • 当计算机执行写入操作时,它通常以4字节的倍数写入(对于32位系统)。执行此操作的一个原因是为了提高性能。因此,当您编写任何数据结构时,如果该数据结构包含前1个字节的变量,然后是4个字节的变量数据,则它将在前1个字节的数据之后进行填充,以使其在32位边界上对齐

    struct {
      union {
        struct {
          uint32_t messageID : 26;
          uint8_t priority : 3; 
        } __attribute__ ((packed));
        uint32_t rawID : 29;
      } __attribute__ ((packed));
      uint8_t canFlags : 3;
    } __attribute__ ((packed)) idSpecial;
    
    现在,在上面的数据结构中,您使用的是
    \uuuuuuu属性(打包))
    这意味着没有填充。因此uint32\u t有4个字节,但您说它有26位和3位作为优先级。现在,由于在一个结构中有两个变量,因此它将保留32位而不是29位,以便第一个结构的信息在边界上对齐


    现在,对于canFlags,它将需要另一个字节。因此,这是5个字节,而不是4个。

    可能重复@DavidItarenco:我认为这篇文章对位域不好。如果在其他地方得到了回答,我也不会感到惊讶。响应者应该注意,OP使用GCC扩展来避免填充。所以,说允许编译器无论如何插入填充是不正确的-当使用
    \uuuu attribute\uuuu((packed))
    时,GCC将组织结构以避免填充(使不支持未对齐读取的平台上的访问成本更高)。简短回答:结构和联合必须是一个字节的倍数,甚至是匿名的。rawid旁边的canFlags使它们成为联合体,我想这不是OP想要的。@Matstpeterson:是的,OP必须重新设计,我只是指出为什么他们当前的设计得到5而不是4。根据你的建议,我们完成了包装
    rawID
    canFlags
    在一个结构中,并在联合体中第一个结构的末尾添加了3位的虚拟填充,现在整个结构如预期的那样大了4个字节。@T.J.Crowder:同意。尽管我看到过出于某种原因这很重要的情况,但我认为可能是MS编译器有这个问题。
    typedef struct structTag {
        struct {
          uint32_t messageID : 26;
          uint8_t priority : 3;
        } __attribute__ ((packed));
        uint8_t canFlags : 3;
    } __attribute__ ((packed)) idSpecial;
    
    struct {
      union {
        struct {
          uint32_t messageID : 26;
          uint8_t priority : 3; 
        } __attribute__ ((packed));
        uint32_t rawID : 29;
      } __attribute__ ((packed));
      uint8_t canFlags : 3;
    } __attribute__ ((packed)) idSpecial;