cgccstructpaddingpacking

In C struct, why packed,aligned seems to do padding?


In C struct, why packed,aligned seems to do padding?

I have to read a bunch of MSB/LSB bytes from a remote device using i2c. As all device data are bytes, I use uint8_t which represent exactly 8 bits that is 1 byte.

Now I need to be sure that the struct is packed (i.e. no "hole" between elements in the struct) unless I would not read data as they are generated in the remote device.

Then, I added aligned for performance (i.e. allow the CPU read the struct with minimum memory access).

As explained here Structure padding and packing, padding aligns structure members to "natural" address boundaries, and, packing prevents from padding.

This simple test surprised me:

$ gcc --version
gcc (Debian 10.2.1-6) 10.2.1 20210110

$ cat dumb.c 
#include <stdio.h>
#include <stdint.h> // uint8_t

struct struct1 {
  uint8_t msb;
} __attribute__((packed));

struct struct2 {
  uint8_t msb;
} __attribute__((packed,aligned(4)));

struct struct3 {
  uint8_t msb;
  uint8_t lsb;
} __attribute__((packed));

struct struct4 {
  uint8_t msb;
  uint8_t lsb;
} __attribute__((packed,aligned(4)));

int main(void) {
  struct struct1 s1;
  printf("sizeof(s1) %zu\n", sizeof(s1));
  struct struct2 s2;
  printf("sizeof(s2) %zu\n", sizeof(s2));
  struct struct3 s3;
  printf("sizeof(s3) %zu\n", sizeof(s3));
  struct struct4 s4;
  printf("sizeof(s4) %zu\n", sizeof(s4));
  return 0;
}

$ gcc -o dumb dumb.c

$ ./dumb 
sizeof(s1) 1
sizeof(s2) 4
sizeof(s3) 2
sizeof(s4) 4

aligned seems to "pad the end of the struct": is this expected?

I read these bytes from a remote device so each byte is transferred using i2c from the device to the PC: now for performance, is it better to go packed only to avoid "extra padding at the end", or, it is better to go packed,aligned transferring and reading extra unused "padded-at-the-end" bytes each time there is a transfer?


Solution

  • The aligned attribute on a struct declaration means that any instance of the struct must begin at an aligned address.

    That also means that if you have an array of structs, the struct may require trailing padding so that each member of the array is properly aligned.

    While the packed attribute will minimize any padding used in the struct, such padding can't necessarily be eliminated completely when used in conjunction with aligned.

    So for example if you had this declaration:

    struct struct2 s2[2];
    

    If you didn't have trailing padding then s2[1] wouldn't be properly aligned.