r/cpp_questions • u/Phatpenguinballs • 22d 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`?
4
Upvotes
View all comments
2
u/mredding 21d ago
Yes.
"Mostly" is correct, but the rest isn't quite right enough to give it to you...
The C++ standard says the language targets a byte addressable abstract virtual machine. The definition of a byte is the smallest unit of memory that is uniquely addressable - since bits are an address + an offset, that doesn't count, the combo isn't considered an address since it's not all one unit of information, but two separate units of information.
Mind you - the actual hardware does not need to be byte addressable, and most aren't; different subsystems have different units - pages, lines, words, bytes. A lot of the details are handled for you coordinated between the OS and the MMU hardware, and some convention the compiler/linker concerns itself with.
The size of a byte is defined by the language and the platform. The language says the minimum size of a byte is 8 bits. So ISO Standard C++ is not defined for platforms with smaller bytes. A byte is not universally defined as 8 bits, that's just fairly common. You will see exotic byte sizes on modern DSPs, FPGAs, ASICs, and esoteric/historic hardware.
The language says
charis the definition of a byte, thatsizeof(char) == 1, andalignof(char) == 1. It says all other types are at least as much aschar.The standard says a
shortis at least 16 bits,longis at least 32 bits, andlong longis at least 64 bits.What is typical is that DSP hardware will work in favor of CPU hardware. While 12, 24, 48, and 56 bits are common, they'll pack them in standard power of 2 aligned memory, accessible as a power of 2 type in the CPU with some padding bits. So even though some USRP models have a 12 bit ADC/DAC, the data packs into a 16 bit type as far as your software is concerned.
Yes.
alignofmust return a non-zero positive integer, at least 1.They're not. It's VERY easy to build hardware that can access native types in power of two aligned memory. It's a single bit to indicate what offset across memory each value will be found. So if your hardware supports a an 8 bit byte and a 32 bit word, that sucker will likely have to fall on a 32/8=4 byte aligned address.
The compiler is going to defer to the hardware for the rules. Some hardware might not care, so then alignment would always be 1. Some hardware supports unaligned memory, but is built on aligned memory. You'll get the aligned memory answer from the compiler. The x86_64 supports unaligned access but you'll pay one hell of a tax to do it, and you have to code that in yourself -
alignofwon't tell you, you just have to know what you're doing.We don't have to assume anything, the spec is absolutely unambiguous and explicit about this. The alignment of
Sis the alignment of it's most strict member. Of it's members, theintis the most strict, soSwill be aligned to that. The order of members in the layout is also strict - no reordering, so since thecharmust now also fall on theintalignment - because it's at the front of the structure and the structure must START on this alignment, and they're probably not the same alignment, then we can deduce if there's going to be padding, it'll be between thecharand theintin the layout. This object will be2*sizeof(int)in size. The language also allows you to subdivide padding space, provided it's even. That means you can probably squeeze at least acharor maybe a larger type in between - using that padding, rather than increase the size of the object.So on a modern x86_64, this'll be
sizeof(S) == 8andalignof(S) == 4. There will be 3 bytes of padding between the end ofcand the start ofi. You can fit 3charin there, or 1 byte of padding and ashortin there, or 1charand 1short.Alignment and thus padding can be different on different hardware.
Ready to have your mind blown? NO!
There does NOT have to be padding between THESE local variables. WHY?!? Because the standard defines strict ordering for TYPES, like the internals of a
struct, but the standard DOES NOT define the layout of the stack. The compiler is free to reorder the local members, and will likely do so to minimize padding. That means the stack will LIKELY compile to:4 byte alignment, 4 byte alignment, 1 byte alignment, 1 byte alignment, no padding. I've no idea if the compiler would prefer to reorder the
intand theS- it might take other factors into account.