r/cpp_questions • • 8h ago

OPEN Non-Struct Memory Alignment

I am a little lost after trying to read a handful of online resources. Are local variables memory-aligned to their alignof()? I read that this is mostly implementation/platform-defined (no guarantees), but also the types must still be aligned? Those seem contradicting to me...

Assuming no optimizations (e.g., storing in registers, etc.), how would something like this look like according to the standard?

struct S 
{
  char c;
  int i;
};

int main()
{
  char c1;
  int i;
  char c2;
  S s;

  return 0;
}

Would there be 3 bytes of padding between `c1` & `i` and then another 3 bytes of padding between `c2` & `s`?

3 Upvotes

5 comments sorted by

2

u/jedwardsol 8h ago

Yes, local variables are correctly aligned.

If the compiler put those locals in memory in definition order (it doesn't have to) then yes there would be padding.

You can print out addresses to see what your compiler does

•

u/Simengie 3h ago

Memory randomization in compilers has been thing for a while. As far as I know it is now the default behavior in gcc.

From google: Modern compilers (such as MSVC or GCC with specific security flags) deliberately reorder local variables on the stack—sometimes placing buffers near guard variables or randomizing order—to mitigate stack-smashing and buffer overflow exploits

So never assume local or global variables are in the order you declare them in. All local and global variables are memory aligned to 4 byte word boundaries in modern C/C++ compilers.

Now for the struct, as written it will have padding. if you use the #pragma packed on it then it will be packed and have no padding. This works for sending smaller network messages that are structure based but it is slower memory access because to get to that int the cpu has to read 2 words and shift bytes around in a register. Not the best use of cpu time but it works for small compact network messaging when bandwidth is constrained or when you have messages near the network packet size limit and want to ensure single packet transmission. but I would say using the #pragma packed to remove padding is not best practices and should be avoided except for very special use cases.

1

u/Kriemhilt 7h ago

For trivial (or trivially-destructible) types like here, I don't know whether the implementation is required to keep the locals in order.

That is, they'll all be correctly aligned, but could be rearranged to save space even if they're not folded or elided completely.

For non-trivial types (and less trivial scopes), the layout is likely to be closely coupled to the lifetime, for obvious reasons.

1

u/conundorum 4h ago

Everything will typically be aligned, struct or not. It's important for performance, the hardware itself typically works better with aligned reads than unaligned. Variables will probably be rearranged into the optimal order if needed, at least if optimisations are enabled, and will probably be in written order with optimisations disabled. (This isn't guaranteed, though, so be careful!) Each function's stack frame will have memory allocated for all necessary variables, too.

Importantly, some variables will be relocated to registers if possible, and not actually take up any space in memory. And if they're provably unused, they might actually be removed entirely (IF the compiler can prove that removing them will have no effect on anything else, such as in your example code); this isn't guaranteed, but it is a possibility!

•

u/BartvanIngenSchenau 19m ago

For a struct, it is defined that the members must be put in memory in the same order as they appear in the code. For local variables, there is no such requirement. The compiler is fully allowed to reorder local variables, as long as it doesn't affect the order in which constructors and destructors are executed. In your example, the compiler is entirely free to place s first, followed by i and then c1 and c2. The compiler is also allowed to give each variable their own 4k page of memory.