Hello! Welcome back.
In our last lesson, we mastered the skill of systematically calculating a C struct's size by accounting for member alignment and padding. We saw that compilers insert padding bytes both between members and at the end of the struct, which often makes the actual size larger than the sum of its parts.
This lesson addresses the logical next question: if the compiler is adding padding, can we influence how much it adds? The answer is yes. Today, we'll focus on how reordering struct members can significantly change the total size and amount of padding. This is a classic interview topic that separates candidates who just know the rules from those who can apply them to write more efficient code.
1. From Calculation to Optimization
You'll recall from our previous session that structc_t had a surprisingly large size.
// Assumed 64-bit system: char=1, double=8, int=4
typedef struct structc_tag {
char c; // 1 byte
double d; // 8 bytes
int s; // 4 bytes
} structc_t;
We calculated its size to be 24 bytes, with a significant amount of that being padding. What happens if we simply change the order of those exact same members?
Let's investigate this by revisiting the resource from our last lesson, which directly compares this inefficient layout with an optimized one.
Structure Member Alignment, Padding and Data Packing
This GeeksforGeeks article provides a perfect side-by-side comparison of two structs with identical members but different ordering, leading to different sizes. This directly demonstrates the principle we're exploring.
Please read the analysis for Structure C and Structure D. Pay close attention to how the memory layout for structd_t avoids the large padding gap seen in structc_t. Then, read the short section 'How to Reduce Structure Padding?'.
As the article demonstrates, simply by reordering the members, the size drops from 24 bytes to 16 bytes. Let's formalize that comparison.
Inefficient Order (structc_t):
char c;at offset 0. (Size: 1 byte)- 7 bytes of padding to align
double. double d;at offset 8. (Size: 8 bytes)int s;at offset 16. (Size: 4 bytes)- 4 bytes of trailing padding to make the total size a multiple of 8 (the struct's alignment).
- Total Size: 24 bytes
Optimized Order (structd_t):
double d;at offset 0. (Size: 8 bytes)int s;at offset 8. (Size: 4 bytes)char c;at offset 12. (Size: 1 byte)- 3 bytes of trailing padding to make the total size a multiple of 8.
- Total Size: 16 bytes
By reordering, we saved 8 bytes of memory. This is a 33% reduction in size with zero change in the actual data stored.
This image shows another example of an inefficiently ordered struct, leading to a significant amount of wasted space.

2. The General Rule for Optimization
The examples above reveal a clear pattern. To minimize padding, you should group members by their size.
The Rule: Order structure members from largest to smallest.
By placing the members with the strictest alignment requirements first (like long long, double, or pointers), you ensure they land on a suitable boundary at the beginning of the struct. The smaller data types can then fill in the space that follows, "soaking up" the area and reducing the need for the compiler to insert large padding gaps.
Let's see another analysis that reinforces this rule.
Structure Padding Isn't Wastage of Memory
This article from dev.to provides another excellent walkthrough of this optimization technique and explains the underlying reasoning very clearly.
Please read the section under the heading 'Packed Structures: When to Use Them (and When Not To)', focusing specifically on the part titled 'Reducing Padding by Reordering Members'. This section clearly explains why reordering is superior to other methods like packing for general use.
The key takeaway from that reading is that reordering is a "compiler-friendly" optimization. Unlike forced packing (which we'll cover next), reordering members doesn't break alignment rules. It simply arranges the data so that the rules can be satisfied with minimal padding.
This means you get a smaller struct without sacrificing performance. The CPU can still access each member in a single, efficient memory cycle. In your field of AUTOSAR and ADAS development, where performance and resource constraints are both critical, this is a fundamental and "free" optimization.
3. Practice Problem
An interviewer puts the following struct on the whiteboard. Assume a 64-bit architecture where uint8_t is 1 byte, uint32_t is 4 bytes, and uint64_t is 8 bytes.
struct SRandomOrder {
uint8_t a;
uint64_t b;
uint8_t c;
uint32_t d;
uint8_t e;
};
They ask you two questions:
- What is the size of this struct?
- How would you rewrite it to be as small as possible without losing data, and what would its new size be?
Take a moment to work through the calculations.
Click for the solution
Part 1: Size of the original struct
- Struct Alignment: Largest member is
uint64_t(8 bytes), so the total size must be a multiple of 8. - Member Placement:
uint8_t a;at offset 0. (Current offset: 1)uint64_t b;needs 8-byte alignment. Add 7 padding bytes.bis at offset 8. (Current offset: 16)uint8_t c;at offset 16. (Current offset: 17)uint32_t d;needs 4-byte alignment. Add 3 padding bytes.dis at offset 20. (Current offset: 24)uint8_t e;at offset 24. (Current offset: 25)
- Trailing Padding: The current size is 25. The next multiple of the struct's alignment (8) is 32. Add 7 padding bytes.
- Final Original Size: 32 bytes.
(Note: My previous thought process for the image LINK was slightly off. Let's stick to the formal calculation. The key point of inefficiency remains.)
Part 2: Optimized struct and its new size
Following the "largest to smallest" rule, the optimized struct is:
struct SOptimizedOrder {
uint64_t b;
uint32_t d;
uint8_t a;
uint8_t c;
uint8_t e;
};
- Struct Alignment: Still 8 bytes.
- Member Placement:
uint64_t b;at offset 0. (Current offset: 8)uint32_t d;at offset 8. (Current offset: 12)uint8_t a;at offset 12. (Current offset: 13)uint8_t c;at offset 13. (Current offset: 14)uint8_t e;at offset 14. (Current offset: 15)
- Trailing Padding: The current size is 15. The next multiple of 8 is 16. Add 1 padding byte.
- Final Optimized Size: 16 bytes.
By reordering, we cut the struct's memory footprint in half.
Conclusion
You've now moved beyond simply analyzing struct memory layouts to actively optimizing them. This is a crucial skill that demonstrates a deep understanding of the interplay between software and hardware.
Key Takeaways:
- The order of members in a C struct has a direct and significant impact on its total size due to alignment padding.
- The optimal arrangement is to declare members in descending order of size (e.g.,
double,int,short,char). - Reordering members reduces memory usage while preserving natural alignment, thus maintaining optimal access performance.
This technique is powerful, but it's not always possible. What if you need to map a struct to a specific memory layout defined by a network protocol or a hardware register, where the order is fixed and non-optimal? In our next lesson, we will explore __attribute__((packed)) and #pragma pack, which give you direct control to eliminate padding, and discuss the critical performance trade-offs that come with it.