user50
user50

Reputation: 273

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

Upvotes: 16

Views: 16721

Answers (6)

DrPrItay
DrPrItay

Reputation: 848

#include <stdio.h>

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


typedef struct __attribute__((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

Upvotes: 0

plugwash
plugwash

Reputation: 10523

While I'm sure there are exceptions, normally compilers insert just enough padding to satisfy alignment requirements, both for the fields within the structure and for the structure as a whole. C compilers are not allowed to re-order fields in a structure.

Different types can have different alignment requirements, the size of a type must be a multiple of it's alignment requirement. On most 64-bit systems, the standard C primitive types will have an alignment requirement equal to their size. Structures will normally have an alignment requirement equal to the highest alignment requirement of their members.

Structures must sometimes be padded to ensure that their members meet alignment requirements and that the size of the structure as a whole is a multiple of it's alignment requirement.

So lets look at your structures, plus a third structure.

struct A
{
    uint32_t var1; //size 4, alignment 4, offset 0
    uint32_t var2; //size 4, alignment 4, offset 4
    uint32_t var3; //size 4, alignment 4, offset 8
    uint32_t var4; //size 4, alignment 4, offset 12
    uint32_t var5; //size 4, alignment 4, offset 16
};

All fields are of type uint32_t, which has a size of 4 and an alignment of 4. Consequently no padding is needed and the structure has an overall size of 20 bytes and overall alignment of 4 bytes.

struct B
{
    uint32_t var1; //size 4, alignment 4, offset 0
    uint32_t var2; //size 4, alignment 4, offset 4
    uint32_t var3; //size 4, alignment 4, offset 8
    //4 bytes of padding.
    uint64_t var5; //size 8, alignment 8, offset 16
};

The first three fields have a size and alignment of 4, so they can be allocated without padding. However var5 has a size and alignment of 8, so it can't be allocated at offset 12. Padding must be inserted and var5 allocated at offset 16. The structure as a whole has a size of 24 and an alignment of 8.

struct C
{
    uint32_t var1; //size 4, alignment 4, offset 0
    uint32_t var2; //size 4, alignment 4, offset 4
    uint64_t var3; //size 8, alignment 8, offset 8
    uint32_t var5; //size 4, alignment 4, offset 16
    //4 bytes of padding
};

In this case all the variables can be allocated to suitable offsets without inserting padding. However the total size of the structure must be a multiple of it's alignment requirement (otherwise arrays would break alignment), so the end of the structure has to be padded. Again the struture as a whole has a size of 24 and an alignment of 8.

Some compilers have mechanisms to override the normal packing of structures, for example the __attribute__((packed)) mentioned in another answer. However these features must be used with extreme caution as they can easily lead to alignment violations.

Upvotes: 0

Kaz
Kaz

Reputation: 58667

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.

Upvotes: 2

rustyx
rustyx

Reputation: 85541

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.).

Upvotes: 16

dbush
dbush

Reputation: 225737

Although compilers are free to pad or not pad as they see fit, typically they will align variables on a boundary that is a multiple of the variable size.

In the case of a struct, the padding is based on the size of the largest primitive element. In the case of the second struct B, that would be var5 which is of type uint64_t.

The layout of struct B with the implicit padding is as follows:

struct B
{
    uint32_t var1;      // offset 0
    uint32_t var2;      // offset 4
    uint32_t var3;      // offset 8
    uint32_t padding;   // offset 12
    uint64_t var5;      // offset 16
};

If var5 were to immediately follow var3, it would be at byte offset 12, which is not a multiple of 8. So there needs to be 4 bytes of padding after var3 to allow var5 to be properly aligned.

In the case of struct A, all fields are 4 bytes in size so no padding is needed. If you created an array of this type, for example struct A a[5], a[1] would be 20 bytes after a[0], a[2] would be 20 bytes after a[1], and so on. Adding padding to struct A would waste space as all subfields are still aligned on the 4 bytes boundary they need.

Upvotes: 0

SmallEgg
SmallEgg

Reputation: 65

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 */
};

Upvotes: 3

Related Questions