Martien Verbruggen said:
I haven't (yet) tried to divine from the standard whether there is any
guarantee about identical initial layout for structs with identical
initial types and orders, but there seems to be quite some code out
there that simply assumes that there is such a guarantee.
C89 3.5.2.1 Structure and Union Specifiers
says:
Each non-bit-field member of a structure or union object
is aligned in an implementation-defined manner appropriate to
its type.
This could be construed as implying that only the types matter for
alignment purposes, and thus that identical type lists would get
identical alignments. However, that "implementation-defined manner"
leaves a lot of leeway.
C89 3.1.2.6 Compatible Type and Composite Type
says:
Moreover, two structure, union, or enumeration types declared in
seperate translation units are compatible if they have the same
number of members, the same member names, and compatible member types;
for two structures, the members shall be in the same order;
for two structures or unions, the bit-fields shall have the same
width; for two enumerations, the members shall have the same values.
This doesn't actually rule out differences within the -same- translation
unit, but as soon as you added another translation unit with the
same structure setup, the second unit would have to be compatible with
both declarations in the first, thus -effectively- ruling out
padding differences unless the compiler were able to prove through
fancy intra-procedure analysis that the compatability of the
structure types did not need to be maintained.
There is another constraint on padding different structures differently:
C89 3.3.2.3 Structure and Union Members
says
With one exception, if a member of a union object is accessed
after a value has been stored in a different member of the object,
the behavior is implementation-defined.[41] One special
guarantee is made in order to simplify the use of unions: If a
union contains several structures that share a common initial
sequence (see below), and if the union object currently contains
one of those structures, it is permitted to inspect the common
initial part of any of them. Two structure share a common initial
sequence if corresponding members have compatible types (and,
for bit-fields, the same widths) for a sequence of one or more
initial members.
{footnote} [41] The "byte orders" for scalar types are invisible
to isolated programs that do not indulge in type punning (for
example, by assigning to one member of a union and inspecting
the storage by accessing another member that is an appropriately
sized array of character type), but must be accounted for when
conforming to externally imposed storage layouts.
I wrote what I did about possible differences in padding with a
simple optimization in mind. Consider this code fragment:
struct demo { long A[2048]; long B[2048] };
demo foo;
int idx;
for (idx=0; idx<sizeof(foo.A)/sizeof(foo.A[0]); i++)
foo.A[idx] = 0;
for (idx=0; idx<sizeof(foo.A)/sizeof(foo.A[0]); i++)
foo.B[idx] = foo.A[idx];
Then on many architectures, foo.A[idx] and foo.B[idx] would share
cache lines, resulting in an inefficient program if A and B were
placed beside each other as implied by mere type alignment concerns.
However, if the compiler inserted unnamed padding of the same width
as a long (or possibly 2 longs on some system), between demo.A and
demo.B, then the cache line sharing of foo.A[idx] and foo.B[idx] would
be broken, resulting in a significantly more efficient program on
those architectures. If foo were a local variable whose address
was never taken (including through the address equivilence of
foo and foo.A), then the compiler would be able to deduce that
type compatability with other translation units was irrelevant and
could [it seems to me] insert padding for the sake of efficiency.