Data Structure Padding

Structure padding and packing

Padding aligns structure members to "natural" address boundaries - say, int members would have offsets, which are mod(4) == 0 on 32-bit platform. Padding is on by default. It inserts the following "gaps" into your first structure:

struct mystruct_A {
char a;
char gap_0[3]; /* inserted by compiler: for alignment of b */
int b;
char c;
char gap_1[3]; /* -"-: for alignment of the whole struct in an array */
} x;

Packing, on the other hand prevents compiler from doing padding - this has to be explicitly requested - under GCC it's __attribute__((__packed__)), so the following:

struct __attribute__((__packed__)) mystruct_A {
char a;
int b;
char c;
};

would produce structure of size 6 on a 32-bit architecture.

A note though - unaligned memory access is slower on architectures that allow it (like x86 and amd64), and is explicitly prohibited on strict alignment architectures like SPARC.

Structure Padding

A char type can be accessed efficiently on any address boundary, so no padding is necessary.

Data structure padding and memory allocation

  1. Maybe, maybe not. You might be on an architecture that likes padding to 8-byte boundaries.
  2. Possibly. Never assume the same, predictable binary representation of a C structure across compilers. Or even across different options in the same compiler.
  3. Maybe. In the example architecture, probably. But the gap may in fact be larger if the compiler's libraries tend to allocate bigger chunks.

How structure padding is works with respect to largest size member in C?

How structure padding is works with respect to largest size member in C?

Padding is fundamentally determined by the alignment requirements of the members, not solely by their sizes. Each complete object type has an alignment requirement, which is some number A such that the address of the object must always be a multiple of A. Alignment requirements are always powers of two.

An object’s size is always a multiple of its alignment requirement, but the alignment requirement is not always equal to the size. For example, an eight-byte double might have four-byte alignment in some C implementations. Alignment requirements typically arise out of hardware considerations, and a system might process eight-byte objects in four-byte chunks whenever it is loading it from memory or storing it to memory, so that hardware would not care about eight-byte alignment even for eight-byte objects. A C implementation designed for that system could make the alignment requirement for an eight-byte double be just four bytes.

For your examples, we will use alignment requirements of one byte for char, four bytes for a four-byte float, and eight bytes for an eight-byte double.

In case 1:

typedef struct{
double A; //8-byte
char B; //1-byte
char C: //1-byte
} Test1;

The structure will always start at the required alignment boundary, because the compiler will give the structure itself an alignment requirement equal to the strictest alignment requirement of any of its members. (Greater than is also allowed by the C standard, but this is not typical in practice.) Then the double A occupies eight bytes. At that point, the char B is at an allowed place, because its alignment requirement is only one byte, so any address is allowed. And char C is also okay. So far, the structure is 10 bytes long. Finally, the structure needs to have an eight-byte alignment so that it can always satisfy the alignment requirement of the double, so the structure’s total size has to be a multiple of eight bytes. (The structure’s total size must be a multiple of its alignment requirement so that in an array of them each element starts at the required alignment.) To accomplish this, we insert six bytes of padding at the end, and the total structure size is 16 bytes.

In case 2:

typedef struct{
int A; //4-byte
double B; //8-byte
float C; //4-byte
} Test2;

int A starts at offset four. Then double B needs to start at a multiple of eight bytes, so four bytes of padding are inserted. Now we are up to 16 bytes: Four for int A, four for padding, and eight for double B. Then float C is at an okay position. It adds four bytes, and we are up to 20 bytes. The structure size needs to be a multiple of eight bytes, so we add four bytes of padding, making 24 bytes total.

In case 3:

typedef struct{
double A; //8-byte
int B; //4-byte [Typo fixed; was "Int".]
float C; //4-byte
} Test3;

double A is eight bytes, and then int B adds four bytes. Now we are at 12 bytes. That is okay for float C, because its alignment requirement is four bytes, and 12 is a multiple of four. This float adds four bytes to the structure, so the size is now 16 bytes. That is okay for the structure’s alignment requirement, eight bytes, because 16 is a multiple of eight. So we do not need to add any padding, and the total structure size is 16 bytes.

Here is the method that compilers commonly use to determine padding in structures:

  • Each member in the structure has some size s and some alignment requirement a.
  • The compiler starts with a size S set to zero and an alignment requirement A set to one (byte).
  • The compiler processes each member in the structure in order:
  1. Consider the member’s alignment requirement a. If S is not currently a multiple of a, then add just enough bytes to S so that it is a multiple of a. This determines where the member will go; it will go at offset S from the beginning of the structure (for the current value of S). The bytes skipped by this addition are called padding bytes.
  2. Set A to the least common multiple1 of A and a.
  3. Add s to S, to set aside space for the member.
  • When the above process is done for each member, consider the structure’s alignment requirement A. If S is not currently a multiple of A, then add just enough to S so that it is a multiple of A.

The size of the structure is the value of S when the above is done.

Additionally:

  • If any member is an array, its size is the number of elements multiplied by the size of each element, and its alignment requirement is the alignment requirement of an element.
  • If any member is a structure, its size and alignment requirement are calculated as above.
  • If any member is a union, its alignment requirement is the least common multiple1 of the alignment requirements of all its members, and its size is the size of its largest member plus just enough to make it a multiple the union’s alignment requirement.

For elementary types (int, double, et cetera), the alignment requirements are implementation-defined and are usually largely determined by the hardware. On many processors, it is faster to load and store data when it has a certain alignment (usually when its address in memory is a multiple of its size). Beyond this, the rules above follow largely from logic; they put each member where it must be to satisfy alignment requirements without using more space than necessary.

Footnote

1 I have worded this for a general case as using the least common multiple of alignment requirements. However, since alignment requirements are always powers of two, the least common multiple of any set of alignment requirements is the largest of them.

Why structure padding is not happening properly?

Your calculations assume that double needs to be 8-byte aligned. That's not the case on all architectures.

On 32bit x86 Linux with GCC, double will be 4-byte aligned by default. You can change that with the -malign-double flag to make it 8-byte aligned.

So the layout assuming defaults on 32bit x86 Linux:

char       // 1 byte
// 3 byte padding
double // 8 bytes
int // 4 bytes

So a total of 16 bytes, with 3 bytes of padding in the middle.

The Wikipedia article Data structure alignment has size/alignment numbers for various types on 32bit x86 and 64bit x86_64 in a few compilers/environments.

structure padding on 64bit machine

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

Understanding Structure Padding in C

In the case of x, it contains a double which (in your case) has size 8. That means that the structure as a whole needs to be a multiple of that size in order for an array of x to be properly aligned.

Since arrays are contiguously allocated, each member of the array comes immediately after the prior one in memory. If x had size 10, then for an array the second element would have the A member at offset 10. For proper alignment, each array member needs to start at a multiple of the size of the largest element. So the structure contains padding at the end to accomplish this.



Related Topics



Leave a reply



Submit