Sizeof pokazuje dobrą wartość – po prostu nie bierzesz pod uwagę wyrównania, które jest w struct.
Generalnie każde pole w struct/class będzie miało "naturalne" wyrównanie do adresu podzielnego przez (zazwyczaj) wielkość danego pola. Do tego sam struct też będzie wyrównany jeśli chodzi o wielkość, tak żeby (w razie alokacji tablicy instancji structów) kolejny struct też był na odpowiednio wyrównanym adresie. Więc ten Twój struct ma de facto 3 elementy:
- int x (4 bajty)
- char c (1 bajt)
- + wyrównanie (3 bajty)
Dzięki temu struct ma 8 bajtów, więc jeśli zaalokujesz tablicę dwóch, to pierwsze pole każdego elementu tablicy ma zagwarantowane, że będzie na adresie podzielnym przez 4 (tak jak wymaga tego "naturalne" wyrównanie intów).
Jeśli się zastanawiasz po co te wyrównania, to generalnie chodzi o dostęp do pamięci. Dostęp do elementów na "naturalnie" wyrównanych adresach jest po prostu szybszy (co wynika z tego, że procesor może pominąć pewne checki związane z granicznymi przypadkami co do cache lines / stron pamięci).
W każdym razie jeśli potrzebujesz z jakiegoś powodu struct który ma dokładnie 5 bajtów (np. odczyt/zapis do pliku, albo wysyłka pakietów sieciowych), to rzuć okiem na:
- #pragma pack
- __attribute__((packed))
Np.
// zachowanie ustawień i wyłączenie wyrównywania
#pragma pack(push,1)
struct asdf {
int x;
char c;
};
// przywrócenie poprzedniego zachowanego ustawienia
#pragma pack(pop)
// sizeof tego structa zwróci 5