The official answer as to how the compiler allocates memory is
"however it wants to". There are a few restrictions, but not
many. In this case, however, what you're seeing is quite
logical: many types have (or may have) alignment restrictions,
and must be placed at an address which is a multiple of some
value. And these restrictions propagate up to any class
which contains members of the type, since otherwise, you
couldn't respect the alignment of the class member. Apparently,
on your machine, bool
has a size of 1 (and char
must have a
size of 1), and int
has a size of 4, and also must be aligned
on an address multiple of 4. So in One::Data
and Two::Data
,
you have an int
, followed by a char
or a bool
, followed by
enough bytes of padding to make the total size of the structure
a multiple of 4. (In principle, the char
/bool
and the
padding can be mixed in any order, but in practice, every
compiler I've seen puts the padding after any declarations.)
Since neither a bool
nor a char
have any alignment
restrictions, there is no need for padding in a class which
contains only one of each.
Note that this depends on both the machine and the compiler. On
some machines (e.g. Sun Sparc or IBM mainframe), accessing a
misaligned value will cause a hardware trap, and the compiler is
almost required to align (and insert padding). On Intel, on the
other hand, a misaligned access will work, but with a noticeable
performance hit; compilers generally force the alignment here
(and both the Windows and Linux binary API's require it), but a
compiler could conceivably ignore it, and some very early Intel
compilers did, back when memory was much tighter than it is
now. (It's actually an interesting question as to which gives
the most performance on a modern machine. If you have a large
array with one of your structures, the extra memory accesses due
to misalignment will probably be resolved from the cache, or
even from the memory read pipeline, at little extra cost,
whereas the smaller size of the object could lead to less cache
misses, and thus better performance. But I've done no measures,
so I'm just guessing.)
Another point to note is that the standard requires that class
members be allocated in order. Technically, only if there's no
access specifier between them, but in practice, all compilers
always allocate them in order. So if you have a class like:
struct T
{
double d1;
char c1;
double d2;
char c2;
};
it will (typically) have a size of 32, where as:
struct T
{
double d1;
double d2;
char c1;
char c2;
};
will only have a size of 24. Back in the days when memory was
tight, we regularly paid attention to such things, but now that
locality is sometimes an issue, maybe it would pay to do so
again: declaring variables in the order of their sizes, with the
biggest ones first.