The compiler aligns the data based on the alignment of the next field of a structure, in your case you have two char
fields that have alignment of 1 (any address is valid to hold a char) but the int
type (32bit) has an alignment requirement of 4bytes, so, as the offset when the second char
element is 2, it needs to add two alignment spaces to make it properly aligned (to an address multiple of 4) and it also makes the whole struct
to be 4 aligned, so when the struct end arrives, there's an alignment of 4 for the next structure of this type (to properly conserve the alignment of the full structure in arrays) and so, it needs to add two more bytes to the end of the structure. This makes the four bytes you observe in your code.
To optimize, just put the large fields first on the structure, and fill it later at the end with the smaller data (that has less alignment requirements)
This way, the structure aligns better with the bigger fields and the fields tend to fill the holes made by alignment. if you had used, instead:
struct my_struct {
double time_as_double; // normally 8 byte alignment
char *timezone; // 8 byte alignment in 64bit architectures.
int year; // 4 byte alignment
unsigned char month, // one byte alignment
mday, // one byte alignment
hour, // one byte alignment
min, // one byte alignment
sec; // one byte alignment
// seven more bytes of alignment to comply with 8 byte alignment for the
// full structure (to allow it to form arrays)
};
you will get a 32 byte structure (a worst case, as it resulted in 25 packed bytes, so the next multiple of 8 is 32)
#include <stdio.h>
struct my_struct {
double time_as_double; // normally 8 byte alignment
char *timezone; // 8 byte alignment in 64bit architectures.
int year; // 4 byte alignment
unsigned char month, // one byte alignment
mday, // one byte alignment
hour, // one byte alignment
min, // one byte alignment
sec; // one byte alignment
// seven more bytes of alignment to comply with 8 byte alignment for the
// full structure (to allow it to form arrays)
};
int main()
{
printf("sizeof (struct my_struct) == %zu\n", sizeof (struct my_struct));
}
which produces:
$ a.out
sizeof (struct my_struct) == 32
$ _