简体   繁体   English

为什么打包结构5的大小而不是4个字节?

[英]Why is the size of packed struct 5 instead of 4 bytes here?

See online example: Ideone example 参见在线示例: Ideone示例

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;

Why would the compiler report the size of the struct as 5 bytes instead of 4 here? 为什么编译器会将结构的大小报告为5个字节而不是4个? It should contain 32 bits. 它应该包含32位。

The problem is that __attribute__((packed)) doesn't perform bitwise packing. 问题是__attribute__((packed))不执行按位打包。 It just guarantees that there is no padding between struct members. 它只是保证struct成员之间没有填充。 You can try this simpler example, where size is also reported as 5: 您可以尝试这个更简单的示例,其中size也报告为5:

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

Bitwise packing is only possible for bitfield members. 按位打包仅适用于位域成员。 You will need to redesign your struct to be a union of a struct with bitfields messageID/priority/canFlags, and a struct with bitfields rowID/canFlags. 您需要将结构重新设计为结构与bitfields messageID / priority / canFlags的结合,以及带有bitfields rowID / canFlags的结构。 In other words, you will need to either have some duplication or resort to accessor macros or member functions. 换句话说,您需要有一些重复或使用访问器宏或成员函数。

It's because of memory alignment: The compiler won't start canFlags in the middle of a byte, it'll start it at the beginning of the next byte (probably*). 这是因为内存对齐:编译器不会在一个字节的中间启动canFlags ,它会在下一个字节的开头(可能是*)启动它。 So you have four bytes for your initial union, and a byte for canFlags . 因此,初始联合有四个字节, canFlags有一个字节。

If, for instance, you moved canFlags into the union, it would (probably*) have size 4: 例如,如果你将canFlags移动到联合中,那么它(可能是*)的大小为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;

Updated example on ideone . 有关ideone的更新示例 Obviously, that specific change probably isn't what you want; 显然,这种具体的变化可能不是你想要的; I'm just demonstrating that the issue is starting a new field not on a byte boundary. 我只是在证明问题是在字节边界上开始一个新字段。


* "probably" because ultimately it's up to the compiler. *“可能”因为最终取决于编译器。

Data is arranged and accessed in computer memory using Data structure alignment. 使用数据结构对齐在计算机存储器中排列和访问数据。 Which has two related issues 其中有两个相关问题

  1. Alignment 对准
  2. Padding 填充

When a write operation is performed by computer, It writes usually in multiple of 4 bytes(for 32 bit systems). 当计算机执行写操作时,它通常以4个字节的多个字节写入(对于32位系统)。 One reason for this act is the goal to increase performance. 这一行为的一个原因是提高绩效的目标。 So when you are writing any data structure, that has first 1 byte variable and then 4 bytes variable data, it will do padding after the first 1 byte data to align it on 32 bit boundaries. 因此,当您编写任何数据结构时,它具有前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;

Now in above data structure you are using __attribute__ ((packed)) which means no padding. 现在在上面的数据结构中你正在使用__attribute__ ((packed)) ,这意味着没有填充。 So uint32_t is of 4 bytes, but you are saing it have 26 bits and 3 bits for priority. 所以uint32_t是4个字节,但是你说它有26位和3位优先级。 Now as you have both variables in one structure so it will reserve 32 bits instead of 29 so that your first structure's information is alligned on boundaries. 现在,因为在一个结构中有两个变量,所以它将保留32位而不是29位,这样您的第一个结构的信息就会在边界上进行分配。

Now for canFlags It will need another bytes. 现在对于canFlags它将需要另一个字节。 So that makes 5 bytes instead of 4. 所以这使得5个字节而不是4个字节。

In some compilers, to "merge" the bits, all items must be of the same type. 在某些编译器中,要“合并”这些位,所有项必须是相同的类型。 So make it uint32_t where you now have uint8_t - this appears to not be the case in the compiler IdeOne uses tho' 所以把它uint32_t你现在有uint8_t - 这似乎不是编译器中的情况IdeOne使用tho'

[No matter what, It is still up to the compiler how it merges the bits, so it the only way to absolutely guarantee that your data is stored as 32 bits is to use a single uint32_t and declare a class that does the relevant shifting and anding/oring to manipulate the value - the only guarantee you have is that ONE element in your struct will have at least as many bits as you have asked for] [无论如何,编译器如何合并这些位仍然取决于它,因此绝对保证数据存储为32位的唯一方法是使用单个uint32_t并声明一个执行相关移位的类。 anding / oring来操纵值 - 唯一的保证是你的struct中的ONE元素至少有你所要求的位数]

As others have pointed out, you can't start a new structure on other than a byte boundary. 正如其他人所指出的那样,除了字节边界之外,你无法启动新结构。 I fixed it by having a second struct inside the union, like this: http://ideone.com/Mr1gjD 我通过在union中包含第二个结构来修复它,如下所示: http//ideone.com/Mr1gjD

#include <stdint.h>
#include <stdio.h>

typedef struct structTag {
  union {
    struct {
      uint32_t messageID : 26; /* 26bit message id, 67108864 ids */
      uint8_t priority : 3; /* priority: MUST BE 0 */
    } __attribute__ ((packed));
    struct {
      uint32_t rawID : 29;
      uint8_t canFlags : 3;
    };
  } __attribute__ ((packed));
} __attribute__ ((packed)) idSpecial;

int main() {
    printf("size: %d", sizeof(idSpecial));
    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM