Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

structure padding on 64bit machine

struct A
{
    uint32_t var1;
    uint32_t var2;
    uint32_t var3;
    uint32_t var4;
    uint32_t var5;
};

In the above structure compiler does not pad and allocates 20 bytes.

now we have another structure which contains one 8 byte variable instead of two 4 bytes.In this case compiler pads and allocates 24 bytes to this structure.

struct B
{
    uint32_t var1;
    uint32_t var2;
    uint32_t var3;
    uint64_t var5;
};

why there is such behaviour? If compiler align the data into 8 byte boundry then there should be padding of 4 bytes in the 1st structure and should not pad the 2nd structure in such case. and also if compiler align the data into the 4 byte boundry then why there is padding of 4 byte in the 2nd structure ?

compiler: GCC Platform: 64 bit linux , x86_64

like image 553
user50 Avatar asked Aug 05 '16 11:08

user50


4 Answers

The rule for alignment (on x86 and x86_64) is generally to align a variable on it's size.

In other words, 32-bit variables are aligned on 4 bytes, 64-bit variables on 8 bytes, etc.

In your second case, 4 bytes of padding are added between

uint32_t var3;
uint64_t var5;

to get var5 to align on 8 bytes.

For this reason it is better to order data members from largest to smallest (but it's not that simple due to data locality, readability etc.).

like image 182
rustyx Avatar answered Oct 20 '22 18:10

rustyx


The padding in your struct B is almost certainly not at the end, but after the third 32-bit member:

struct B
{
    uint32_t var1;
    uint32_t var2;
    uint32_t var3;
    // 4-byte padding here
    uint64_t var5;
};

This is because var1 through var3 add up to 12 bytes, which is not divisible by 8. Your compiler wants an 8 byte integer type to be on an address divisible by 8.

You will also get padding at the end of the structure in a situation like this:

struct C
{
   uint64_t memb1;
   uint32_t memb2;
   // Padding here
};

That padding is for the sake of memb1 alignment in an array of struct C:

struct C c_array[13];

of course c_array[0].memb1 is aligned since it is at the base address of the array. But what about c_array[1].memb1? Without the padding in the structure, it would not be aligned.

C is defined in such a way that padding may not be added between array elements; the elements of an array are tightly allocated. Therefore, if padding is required it must be shoehorned into the element type. The layout of structs has to take into account possible array aggregation.

like image 36
Kaz Avatar answered Oct 20 '22 19:10

Kaz


#include <stdio.h>

typedef struct __atribute__((packed)) A {
    uint32_t var1;
    uint32_t var2;
    uint32_t var3;
    uint32_t var4;
    uint32_t var5;
} A ;


typedef struct __atribute__((packed)) B {
    uint32_t var1;
    uint32_t var2;
    uint32_t var3;
    uint64_t var4;
} B;


int main()
{
    printf("sizeof(A): {%d} sizeof(B): {%d}", sizeof(A), sizeof(B));

    return 0;
}

try this, it worked for me

like image 34
DrPrItay Avatar answered Oct 20 '22 20:10

DrPrItay


First of all, structure alignment is not an exact science and may depend on architecture and compiler.

In many case, all structure members are padded according the biggest variable (in byte). On your first structure, all variables are uint32_t, wich is 4 bytes length. Then, your structure size is equal to sizeof(uint32_t) * 5 = 4 * 5 = 20.

On your second structure, the biggest element is uint64_t, wich has a size of 8 bytes. So all elements will be padded according 8 bytes.

The first two uint32_t are padded together, but the third one can't be padded properly : if it was padded with the next integer, the uint64_t would be split in two ! So the compiler decided to let this uint32_t on it 's own to avoid splitting the uint64_t.

Here is an example with your structures and what the address of all variables could be:

struct A
{
  uint32_t var1;   /* ..00 */
  uint32_t var2;   /* ..04 */
  uint32_t var3;   /* ..08 */
  uint32_t var4;   /* ..12 */
  uint32_t var5;   /* ..16 */
};

struct B
{
  uint32_t var1;   /* ..00 */
  uint32_t var2;   /* ..04 */
  uint32_t var3;   /* ..08 */
  uint64_t var5;   /* ..16 */
};
like image 30
SmallEgg Avatar answered Oct 20 '22 20:10

SmallEgg