question about __attribute__((__packed__)), not the size I am expecting

Thread Starter

bug13

Joined Feb 13, 2012
1,889
Hi guys,

Given the code below, I expect the both to be 4 bytes, but somehow the first one is 5 bytes. What do I missing here? Assuming there is a topic here somewhere I don't understand.

Compiler: gcc v5.1.0, mingw32
machine: x86-64bit

It does the same on an ARM as well.


C:
typedef struct TEST{
    uint8_t     data1;
    uint16_t    data2;
    uint8_t     data3;
}__attribute__((__packed__)) test_t;

typedef struct TEST2{
    uint16_t    data2;      /* moved this to top */
    uint8_t     data1;
    uint8_t     data3;
}__attribute__((__packed__)) test2_t;


int main(int argc, char **argv){

    /*! I expect them both to be 4 bytes
     *  Why the first one is 5 bytes??
     */  

    printf("size of test_t   %zu\n", sizeof(test_t));   /* 5 bytes */
    printf("size of test2_t  %zu\n", sizeof(test2_t));  /* 4 bytes */


    return 0;
}
 

Thread Starter

bug13

Joined Feb 13, 2012
1,889

nsaspook

Joined Aug 27, 2009
7,744
Packing doesn't guarantee no padding in the default compiler alignment for data types. Declare the structure members in their increasing/decreasing order of size to minimize padding.

http://www.catb.org/esr/structure-packing/
Sometimes you can coerce your compiler into not using the processor’s normal alignment rules by using a pragma, usually #pragma pack. GCC and clang have a "packed" attribute you can attach to individual structure declarations; GCC has an -fpack-struct option for entire compilations.

Do not do this casually, as it forces the generation of more expensive and slower code. Usually you can save as much memory, or almost as much, with the techniques I describe here.

The only good reason for #pragma pack is if you have to exactly match your C data layout to some kind of bit-level hardware or protocol requirement, like a memory-mapped hardware port, and violating normal alignment is required for that to work. If you’re in that situation, and you don’t already know everything else I’m writing about here, you’re in deep trouble and I wish you luck.
 

Thread Starter

bug13

Joined Feb 13, 2012
1,889
uint16_t data2;

This element of the struct MUST be on an even address so the data1 has a byte inserted after it.

Packing doesn't guarantee no padding in the default compiler alignment for data types. Declare the structure members in their increasing/decreasing order of size to minimize padding.

http://www.catb.org/esr/structure-packing/
I see, thanks for explaining. Looks like another trap for young player! Lucky I wasn't doing memory-mapped register. Just packing them up for serialization and CRC calculation.

Thanks again! :)
 

MrSoftware

Joined Oct 29, 2013
1,937
Look at the addresses of each element and that will tell you where the padding is.

Also I would be curious to see if using #pragma pack(0) would pack the top structure tightly.
 
Last edited:

MrSoftware

Joined Oct 29, 2013
1,937
An example of compiler version matters, I tried to repeat your results using the free online compiler here:

https://www.onlinegdb.com/online_c_compiler

But alas it packed both structs tightly, at least with the default settings. Without the pack attribute the top struct is 6 bytes on this compiler, so it is definitely minding the attribute.

1582234320190.png
 

Thread Starter

bug13

Joined Feb 13, 2012
1,889
An example of compiler version matters, I tried to repeat your results using the free online compiler here:

https://www.onlinegdb.com/online_c_compiler

But alas it packed both structs tightly, at least with the default settings. Without the pack attribute the top struct is 6 bytes on this compiler, so it is definitely minding the attribute.
Hmmm... maybe my compiler it too old. I should update it then.
 

Thread Starter

bug13

Joined Feb 13, 2012
1,889
Look at the addresses of each element and that will tell you where the padding is.

Also I would be curious to see if using #pragma pack(0) would pack the top structure tightly.
Run a couple test on the same machine that run the first test in my OP. If I do #pragma pack(1), both are 4 bytes. If I use #pragma pack(0), it seems to have no effect (eg, 6 bytes for first, and 4 bytes for the second)

Here is my test code:
C:
/*
gcc version 5.1.0
mingw32
*/

#pragma pack(1)            /* both 4 bytes */
//##pragma pack(0)        /* seems to have no effect */
typedef struct TEST{
    uint8_t     data1;
    uint16_t    data2;
    uint8_t     data3;
}test_t;

typedef struct TEST2{
    uint16_t    data2;      /* moved this to top */
    uint8_t     data1;
    uint8_t     data3;
}test2_t;
#pragma pack()

int main(int argc, char **argv){
    printf("size of test_t   %zu\n", sizeof(test_t));   /* 5 bytes */
    printf("size of test2_t  %zu\n", sizeof(test2_t));  /* 4 bytes */
    return 0;
}
 
Last edited:
Top