Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

See online example: Ideone example

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? It should contain 32 bits.

like image 600
c0dehunter Avatar asked Apr 01 '15 07:04

c0dehunter


People also ask

What is the size of struct always?

The sizeof for a struct is not always equal to the sum of sizeof of each individual member. This is because of the padding added by the compiler to avoid alignment issues. Padding is only added when a structure member is followed by a member with a larger size or at the end of the structure.

How do you determine the size of a struct?

The size is at least sizeof(int) + sizeof(struct node *) + sizeof(struct node *) . But it may be more as the compiler is allowed to add padding bytes to your structure if it wishes.

How many bytes does a struct take?

Above is the alignment of the structure A, and that's why the size of the struct is 32 Bytes. Also, the object a of type struct A is 32 Bytes.

What is the size of struct always in C?

The size of the entire structure is 8 bytes. On knowing the structured padding, it is easier to redesign or rewrite the structure.


2 Answers

The problem is that __attribute__((packed)) doesn't perform bitwise packing. It just guarantees that there is no padding between struct members. You can try this simpler example, where size is also reported as 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. In other words, you will need to either have some duplication or resort to accessor macros or member functions.

like image 75
user4815162342 Avatar answered Oct 06 '22 12:10

user4815162342


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*). So you have four bytes for your initial union, and a byte for canFlags.

If, for instance, you moved canFlags into the union, it would (probably*) have size 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. 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.

like image 23
T.J. Crowder Avatar answered Oct 06 '22 12:10

T.J. Crowder