The basic rules are simple:
- members should be there in order (if in C ++ you are not using private: public: ... sections)
- addition allowed between members after the last
What about that. The rest remains to be implemented: storage by type, fill size. You can usually expect it to be properly documented in the ABI or directly in the compiler, and even have tools for manipulation.
In practice, an addition is required on some architectures, say, SPARC requires 32-bit "ints" aligned to an address divisible by 4. On others, this is not a requirement, but inconsistent entities may take longer to process, say, the 80286 processor executes an additional cycle to read a 16-bit object from an odd address. (Before I forget: the presentation of the types themselves is different!)
Usually the alignment requirement or the best performance matches exactly: you have to align the border in the same way as the size. A good counter example is the number of 80-bit floating point numbers (available as double or long doubles in some compilers) that look like 8 or 16 bytes, not 10.
To play with the compiler, you can usually switch to the default installation. This varies from version to version, so it is better taken into account when upgrading. And an internal code redefinition tool like _attribute__(packed) in gcc and #pragma in the MS package, and many others. All of these are obviously standards.
The bottom line is that if you want to tinker with the layout, you start reading the dox of all the compilers that you aim at, now and in the future, to know what they are doing and how to manage it. You may also want to read the dox of the target platforms, depending on why you are interested in the layout in the first place.
One common motivation is to have a stable layout when you write raw memory to a file and expect to read it. Perhaps another platform uses a different compiler. This is easier until a new type of platform appears in the scene.
Another motivation is productivity. This method is more complicated, because the rules change quickly, and the effect is difficult to predict immediately. Say, for Intel, the basic “inconsistent" punishment lasts a long time, instead it is important that it is inside the cache line. Where the cache line size depends on the processor. In addition, using more add-ons can lead to improvements in the individual, while fully packaged structures are more economical to use cache.
And some operations require proper alignment, but are not directly applied by the compiler, you may need to use special alignment pragmas (for example, for certain SSEs ).
The bottom line is repeated: stop guessing, solve your goals and read the correct dox. (By the way, for me I read architecture manuals for SPARC , IA32 , while others were amazing and winning in many ways.)