Question

Why struct sizeof Is Larger Than the Sum of Its Members in C

ccppstructsizeofc++-faq

Question

In C, why can the sizeof operator return a value for a struct that is larger than the total of the sizeof values of its individual members?

For example, why might a structure occupy more memory than you would expect by simply adding the sizes of all its fields together?

Short Answer

By the end of this page, you will understand why sizeof(struct) is often larger than the sum of its members in C. You will learn about memory alignment, padding bytes, and how compilers lay out structures in memory for performance and correctness. You will also see examples, common mistakes, and practical ways to inspect and reason about struct size.

Concept

In C, a struct groups multiple values into one type. It may seem natural to assume that the size of a structure should be exactly the sum of the sizes of its members, but that is often not true.

The main reason is alignment.

Different data types usually have preferred memory boundaries where they can be accessed more efficiently. For example:

  • a char often has alignment 1
  • an int often has alignment 4
  • a double often has alignment 8

To satisfy these alignment requirements, the compiler may insert padding bytes between members or at the end of the structure.

Why alignment matters

Processors are often faster when data is stored at addresses aligned to the data type's natural boundary. On some systems, misaligned access is slower. On others, it can even cause hardware faults.

So the compiler arranges structure members like this:

  1. Place each member at an address that satisfies its alignment requirement.
  2. Insert padding if necessary before the next member.
  3. Possibly add padding at the end so that arrays of that structure are also properly aligned.

Example idea

Suppose this structure exists on a system where int is 4 bytes and aligned to 4 bytes:

struct Example {
    char a;
    int b;
};
  • a uses 1 byte
  • b uses 4 bytes
  • But b usually wants to start at an address divisible by 4

So the compiler may insert 3 padding bytes after a.

That makes the total size:

  • 1 byte for a
  • 3 bytes of padding
  • 4 bytes for b
  • total: 8 bytes

Not 5.

End padding also matters

Consider:

struct Item {
    int id;
    char flag;
};

This might look like 4 + 1 = 5 bytes. But the compiler may make it 8 bytes instead:

  • id: 4 bytes
  • flag: 1 byte
  • trailing padding: 3 bytes

Why add padding at the end? Because if you create an array of struct Item, every element should start at a properly aligned address.

Important point

The C language does not guarantee one exact byte layout across all systems. Structure layout depends on:

  • compiler
  • target architecture
  • ABI rules
  • member order
  • alignment requirements of each type

That is why sizeof(struct) is about the actual memory layout chosen by the compiler, not just the raw sum of field sizes.

Mental Model

Think of a struct like packing objects into storage boxes on a shelf.

  • A small item like a char is like a coin.
  • A larger item like an int is like a book that must sit flat on a shelf section of a certain width.

If you put a coin down first and then want to place a book, you may need to leave some empty space so the book starts in the correct position. That empty space is padding.

At the end of the box, you may also leave some space so that if you line up many identical boxes in a row, each box starts neatly at the right boundary. That is trailing padding.

So sizeof(struct) measures the full box, including the empty spaces needed for neat and efficient placement, not just the items inside it.

Take Quiz

Syntax and Examples

In C, you use sizeof to measure the size of a type or object.

sizeof(int)
sizeof(struct MyStruct)
sizeof(variable)

Example 1: Padding between members

#include <stdio.h>

struct Example {
    char a;
    int b;
};

int main(void) {
    printf("sizeof(char) = %zu\n", sizeof(char));
    printf("sizeof(int) = %zu\n", sizeof(int));
    printf("sizeof(struct Example) = %zu\n", sizeof(struct Example));
    return 0;
}

A common result is:

sizeof(char) = 1
sizeof(int) = 4
sizeof(struct Example) = 8

Even though 1 + 4 = 5, the structure may be 8 bytes because of 3 padding bytes inserted before b.

Example 2: Member order affects size

#include <stdio.h>

struct A {
    char c;
    int i;
    char d;
};

struct B {
    int i;
    char c;
    char d;
};

int main(void) {
    printf("sizeof(struct A) = %zu\n", sizeof(struct A));
    printf("sizeof(struct B) = %zu\n", sizeof(struct B));
    return 0;
}

On many systems:

  • struct A may be 12 bytes
  • struct B may be 8 bytes

This happens because grouping larger members first can reduce padding.

Example 3: Inspect offsets with offsetof

#include <stdio.h>
#include <stddef.h>

struct Example {
    char a;
    int b;
    char c;
};

int main(void) {
    printf("offset of a: %zu\n", offsetof(struct Example, a));
    printf("offset of b: %zu\n", offsetof(struct Example, b));
    printf("offset of c: %zu\n", offsetof(struct Example, c));
    printf("sizeof(struct Example): %zu\n", sizeof(struct Example));
    return 0;
}

This helps you see where each member starts in memory.

Step by Step Execution

Consider this example:

#include <stdio.h>

struct Sample {
    char x;
    int y;
};

int main(void) {
    struct Sample s;
    printf("%zu\n", sizeof(s));
    return 0;
}

Let us walk through what often happens on a system where int is 4 bytes and aligned to 4 bytes.

Step 1: Start the struct at offset 0

The structure begins at memory offset 0.

Step 2: Place x

char x;
  • x needs 1 byte
  • it is placed at offset 0

Current used space: 1 byte.

Step 3: Place y

int y;
  • y needs 4 bytes
  • y should start at an offset divisible by 4
  • the next free offset is 1, which is not aligned for int

So the compiler inserts padding at offsets 1, 2, and 3.

Now y starts at offset 4 and occupies offsets 4 to 7.

Step 4: Total size so far

The structure now uses offsets 0 through 7, so its size is 8 bytes.

Memory layout view

OffsetContent
0x
1padding
2padding
3padding
4y byte 1
5y byte 2
6y byte 3
7y byte 4

Final result

sizeof(struct Sample) == 8

Even though the members only add up to 5 bytes, the actual structure size is 8 because alignment rules require padding.

Real World Use Cases

Understanding struct padding matters in many practical situations.

Binary file formats

If you write a struct directly to a file, padding bytes become part of the file layout. This can make the file format compiler-dependent and non-portable.

Network protocols

When sending binary data over a network, relying on raw struct layout is risky. Different systems may use different padding and alignment rules.

Embedded systems

Memory is often limited, so unnecessary padding can matter. Developers may reorder fields to reduce wasted space.

Performance-sensitive code

Alignment can improve CPU performance. Sometimes a slightly larger struct is faster because aligned accesses are more efficient.

Interfacing with hardware or foreign code

When matching a C struct with hardware registers, operating system APIs, or code written in another language, exact layout matters. Developers must understand alignment and packing rules carefully.

Take Quiz

Real Codebase Usage

In real projects, developers usually do not just ask "what is the sum of the fields?" They care about layout, portability, and intent.

Common patterns

  • Reordering fields to reduce padding
  • Using offsetof to inspect layout when necessary
  • Avoiding direct serialization of raw structs
  • Defining explicit binary formats instead of relying on compiler layout
  • Adding static checks in low-level code

Example: reorder to reduce wasted space

struct LessEfficient {
    char a;
    int b;
    char c;
};

struct MoreEfficient {
    int b;
    char a;
    char c;
};

The second version may use less memory on many systems.

Example: validating assumptions

#include <assert.h>

struct Header {
    int id;
    short type;
    short flags;
};

int main(void) {
    assert(sizeof(struct Header) == 8);
    return 0;
}

This kind of check can be useful in low-level code, but it should only be used when the target platform assumptions are intentional.

Example: explicit serialization

Instead of writing a struct directly:

fwrite(&header, sizeof header, 1, file);

many codebases prefer to write each field in a defined format so the data stays portable and predictable.

Common Mistakes

Mistake 1: Assuming member sizes always add up exactly

Broken assumption:

struct Data {
    char tag;
    int value;
};

/* Wrong expectation: sizeof(struct Data) == 5 */

Why it happens:

  • ignores alignment and padding

How to avoid it:

  • always use sizeof(struct Data) if you need the real size
  • do not manually guess struct size

Mistake 2: Using raw structs as portable file or network formats

Broken approach:

fwrite(&data, sizeof data, 1, file);

Why it is risky:

  • layout can differ across compilers and platforms
  • padding bytes may be included
  • endianness may also differ

How to avoid it:

  • serialize each field explicitly
  • define exact byte-level formats

Mistake 3: Forgetting trailing padding in arrays

struct Item {
    int id;
    char flag;
};

A beginner may expect an array of 10 items to use 10 * 5 = 50 bytes. But if sizeof(struct Item) is 8, the array uses 80 bytes.

How to avoid it:

  • use sizeof(struct Item) when calculating array memory

Mistake 4: Using packed structs without understanding the trade-offs

Some compilers support packed structures, for example with compiler-specific attributes or pragmas.

Problem:

  • packed structs can reduce size
  • but they may cause slower access or even unsafe access on some hardware

How to avoid it:

  • only use packing when exact layout is required
  • treat packing as compiler-specific and platform-sensitive

Mistake 5: Assuming layout is identical everywhere

struct Example {
    char a;
    long b;
};

The size of long and its alignment can vary between systems.

How to avoid it:

  • do not hard-code assumptions unless you control the platform
  • check your compiler and target ABI if exact layout matters

Comparisons

ConceptWhat it meansIncludes padding?Portable across systems?
Sum of member sizesAdd sizeof for each field manuallyNoNo
sizeof(struct T)Actual memory size chosen by compilerYesDepends on platform
offsetof(struct T, member)Position of a member inside the structShows layout gaps indirectlyDepends on platform
Packed structCompiler-specific layout with reduced/removed paddingOften less or noneLess portable

sizeof(member sum) vs sizeof(struct)

  • Sum of members is just a rough estimate.
  • sizeof(struct) is the real amount of memory an object of that type uses.

Normal struct vs packed struct

TypeAdvantageDisadvantage
Normal structEfficient aligned access, standard behaviorMay waste space due to padding
Packed structSmaller memory layoutCan be slower, unsafe on some systems, compiler-specific

Member order comparison

Layout styleResult
Small and large fields mixed randomlyOften more padding
Larger fields first, smaller fields laterOften less padding
Take Quiz

Cheat Sheet

sizeof(struct T)
offsetof(struct T, member)

Key rules

  • sizeof(struct) may be larger than the sum of member sizes.
  • The extra space usually comes from padding.
  • Padding is added to satisfy alignment requirements.
  • Padding can appear:
    • between members
    • at the end of the struct
  • Member order can change the final size.
  • Arrays of structs rely on the struct size including any trailing padding.

Example

struct X {
    char a;
    int b;
};

Common layout:

  • a at offset 0
  • padding at offsets 1 to 3
  • b at offset 4
  • total size: 8

Tips

  • Use sizeof(struct T) for real size.
  • Use offsetof to inspect member positions.
  • Do not assume raw struct layout is portable.
  • Reorder fields if reducing padding matters.
  • Be careful with compiler-specific packed structs.

Common edge cases

  • long, pointers, and double may have different sizes on different systems.
  • Trailing padding matters for arrays.
  • Exact layout depends on compiler and architecture.

FAQ

Why is sizeof(struct) bigger than the sum of its fields in C?

Because the compiler may add padding bytes to satisfy alignment requirements for faster or valid memory access.

What is padding in a struct?

Padding is unused space inserted by the compiler between members or at the end of a structure.

Why is padding added at the end of a struct?

Trailing padding helps ensure that each element in an array of structs starts at a properly aligned address.

Can I remove struct padding?

Sometimes, with compiler-specific packing features or by reordering members. But removing padding can reduce performance or cause portability problems.

Does every compiler produce the same struct size?

No. Struct layout can vary by compiler, architecture, and ABI.

How can I see where struct members are stored?

Use offsetof() from <stddef.h> to inspect member offsets.

Should I write structs directly to files or sockets?

Usually no, unless you fully control the platform and layout. Explicit serialization is safer and more portable.

Take Quiz

Mini Project

Description

Build a small C program that compares the expected sum of structure member sizes with the actual sizeof result. This project helps you see padding and alignment in action and teaches you how member order affects memory usage.

Goal

Create a program that prints member sizes, member offsets, and total struct sizes for two similar structs with different field orders.

Requirements

  1. Define two structs containing the same members in different orders.
  2. Print the sizeof of each member type used.
  3. Print the offsetof value for each member in both structs.
  4. Print the total sizeof of each struct.
  5. Show which struct is more memory-efficient on your system.
Take Quiz

Keep learning

Related questions

View more