Understanding Structure Padding and Size Calculation in C

Fundamental data types in C occupy specific byte sizes: char (1 byte), short (2 bytes), int (4 bytes), long (4 bytes), long long (8 bytes), float (4 bytes), and double (8 bytes). However, when these types are combined into structures, the resulting memory footprint may exceed the sum of individual member sizes due to a critical mechanism called memory alignment.

Consider these two structure definitions:

struct SampleA
{
    int primary;
};

struct SampleB
{
    char indicator;
    int payload;
};

int main()
{
    printf("Size of SampleA: %zu\n", sizeof(struct SampleA));
    printf("Size of SampleB: %zu\n", sizeof(struct SampleB));
    return 0;
}

The first structure consumes exactly 4 bytes, which matches the size of its single int member. The second structure, however, occupies 8 bytes despite containing only 1 + 4 = 5 bytes of actual data. This 3-byte discrepancy stems from memory alignment requirements that modern processors enforce for optimal performance.

Why Alignment Matters

Processors access memory most efficiently when data resides on "natural boundaries"—addresses divisible by the data size. When a 4-byte integer is stored at an address not divisible by 4, the processor must perform additional operations to retrieve or store that value. This involves reading from two adjacent memory locations and combining the parts, which significantly impacts performance in memory-intensive applications.

The image below illustrates this concept. In the top representation, bytes are packed contiguously, placing the int member at address 101. Since 101 is not divisible by 4, the processor cannot fetch the value in a single operation. The bottom representation introduces padding bytes, ensuring the int member starts at address 104 (divisible by 4), enabling direct access.

Modern operating systems and compilers prioritize speed over space efficiency. A small amount of wasted memory provides substantial performance gains, which is why alignment is the standard approach in virtually all production C implementations.

Alignment Rule 1: Member Address Requirements

Rule 1: Each member's starting address must be divisible by the member's own size.

This means:

  • char members can reside at any address (divisible by 1)
  • short members must start at even addresses (divisible by 2)
  • int members require addresses divisible by 4
  • long and pointer types follow their respective size requirements

Let's examine several structure definitions and calculate their sizes:

struct DataBlock1 {
    char flag;      // 1 byte at address +0
    // 3 bytes padding
    int value;      // 4 bytes at address +4
};

struct DataBlock2 {
    char alpha;     // 1 byte at address +0
    // 1 byte padding
    short beta;     // 2 bytes at address +2
    // 2 bytes padding
    int gamma;      // 4 bytes at address +4
};

struct DataBlock3 {
    short x;        // 2 bytes at address +0
    char y;         // 1 byte at address +2
    // 1 byte padding
    int z;          // 4 bytes at address +4
};

The calculated sizes are 8, 12, and 12 bytes respectively. The numbers following "+" in memory layout diagrams indicate padding bytes that remain unused but occupy space in the structure.

The Single Member Exception

Consider this structure with two members of different types:

struct Payload {
    int identifier;  // 4 bytes at address +0
    char type;       // 1 byte at address +4
};

Logically, this should occupy 5 bytes. However, the actual size is 8 bytes. Rule 1 explains the member positions, but another rule determines the final structure size.

Alignment Rule 2: Structure Size Must Be a Multiple of Largest Member

Rule 2: The total size of a structure must be divisible by the size of its largest member.

In the Payload structure, the largest member (int) is 4 bytes. The logical size of 5 bytes is not divisible by 4, so the compiler adds 3 padding bytes to reach 8 bytes, which is divisible by 4. This adjustment ensures that every instance of the structure satisfies alignment requirements.

Why Rule 2 Exists: Array Element Considerations

The true purpose of Rule 2 becomes clear when structures are used in arrays:

struct Payload {
    int identifier;
    char type;
};

int main()
{
    struct Payload dataset[2];  // Array of two structures
    return 0;
}

Structures can be organized in contiguous memory as arrays. If Payload were 5 bytes, the memory layout would place the first element's int at an aligned address, but the second element's int would start at an offset that violates alignment requirements.

When 5-byte structures are arranged in memory, the second element's int member begins at address 105 (if the first starts at 100). Since 105 is not divisible by 4, accessing this member would require the slower multi-read operation discussed earlier.

By extending the structure to 8 bytes, both array elements have their int members at properly aligned addresses (100 and 108, both divisible by 4). This enables efficient access to any element in the array without special handling.

The compiler enforces Rule 2 precisely to ensure that array indexing produces correctly aligned members throughout the entire array.

Tags: C memory-alignment Struct data-structures programming

Posted on Fri, 18 Sep 2026 16:36:38 +0000 by tazdevil