The C standard does not have any rule that int32_t
must have any particular alignment. Alignment requirements are implementation-defined. Each C implementation may choose what its alignment requires are (with some constraints and, to conform to the standard, it must document them).
It is odd the example shows two bytes of padding between the a
and b
members, as these bytes are apparently not needed for padding if int32_t
does not require four-byte alignment. The standard allows implementations to insert padding between members even if it is not needed for alignment, but implementations do not normally do that.
Overall, you should not give this too much concern. It is most likely an example that was not fully thought out.
However, one way this can arise is using GCC’s packed
attribute:
struct myStruct
is declared as shown in the example.
- The C implementation normally requires
int32_t
to have four-byte alignment, so it lays out the structure with two bytes of padding between members a
and b
.
- A further structure is declared:
struct foo
{
char x[3];
struct myStruct s;
} __attribute__((__packed__));
This declares foo
to be packed, meaning the normal alignment requirement of its members is suppressed, and the members are packed into consecutive bytes with no padding. While this means the struct myStruct
member s
is put into consecutive bytes after x
, internally it retains its original layout. It has to keep its original layout to be a proper struct myStruct
even though its alignment requirement is suppressed. For example, if we executed this code:
struct myStruct m = { some initialization };
struct foo f;
memcpy(&f.s, &m, sizeof m);
then we would want the memcpy
to reproduce m
in f.s
.
Compiling and this code:
#include <stdint.h>
#include <stdio.h>
int main(void)
{
struct myStruct { char a[2]; int32_t b; int16_t c; };
struct foo
{
char x[3];
struct myStruct s;
} __attribute__((__packed__));
struct foo f;
printf("%p\n", (void *) &f.s.b);
}
in a C implementation that normally requires four-byte alignment for int32_t
produces output of “0x7ffee7e729f7” in one run, showing that the b
member is at an address that is 3 modulo 4.