#include <stdio.h>
int main(void)
{
printf("%d\n", sizeof(char));
printf("%d\n", sizeof(short));
printf("%d\n", sizeof(int));
printf("%d\n", sizeof(long));
printf("%d\n", sizeof(long long)); // c99
printf("%d\n", sizeof(void*));
return 0;
}
U mnie wyniki są następujące: 1,2,4,8,8. Możliwe że uzyskaliście inne. Od czego zależy więc rozmiar danych? Od architektur komputerów na których program został uruchomiony, bądź systemu operacyjnego. Teoretycznie tak, jednak nie zapominajmy o tym że rozmiar danych zależy przede wszystkim od kompilatora. Przykładowo, jeśli mamy 64-bitowy procesor, możemy używać 16-bitowego kompilatora, w którym to int czy void* będzie miał rozmiar właśnie 16 bitów.
Ogólnie powinna zachodzić następująca zależność:
- sizeof(char) ≤ sizeof(short) ≤ sizeof(int) ≤ sizeof(long) ≤ sizeof(long long)
Istnieją jednak minimalne progi rozmiarowe ustalane na podstawie minimalnego zakresu:
- sizeof(char) - minimum 1 bajt
- sizeof(short) - minimum 2 bajty
- sizeof(int) - minimum 2 bajty
- sizeof(long) - miniumum 4 bajty
- sizeof(long long) - minimum 8 bajtów (dostępne od standardu c99)
Jeśli chodzi o sizeof(void*) rozmiar zależny jest od szerokości słowa. Czyli dla kompilatora:
- 16-bitowego - 2 bajty
- 32-bitowego - 4 bajty
- 64-bitowego - 8 bajtów
A jak sprawa wygląda ze strukturami? Interesująco. Zaczniemy powoli. Wszystko omawiać będę biorąc pod uwagę rozmiary danych, które występują u mnie (char: 1, short: 2, int: 4 - GCC 64bit x86).
struct A{
char znak;
};
Tutaj sizeof(struct A) zwróci 1 - nic zaskakującego.
struct B{};
Nie wszystkie kompilatory zezwolą nam na zdefiniowanie takiej struktury (GCC pozwoli), jednak jeśli pozwolą to sizeof(struct B) zwróci 0 jeśli skompilujemy plik z rozszerzeniem *.c, natomiast gdy plik będzie miał rozszerzenie *.cpp zwróci 1.
struct C{
char znak; // 1 bajt
// 1 bajt wyrównawczy - wyrównanie do 2
// 1 bajt wyrównawczy - wyrównanie do 2
short liczba; // 2 bajty
};
Wydawałoby się, że skoro sizeof(char) == 1, a sizeof(short) == 2, sizof(struct C) == 3. Sprawdźcie więc wyniki. Okazuje się, że zostaje nam zwrócony rozmiar większy niż suma rozmiarów danych użytych w strukturze. Wszystkiemu winny jest kompilator, który dodaje odpowiednią ilość bajtów w celu wyrównania danych w pamięci (data structure alignment), zapewnia to szybszy odczyt danych przez procesor. Ile zostaje dodanych bitów wyrównawczych? Dokładnie tyle by uzupełnić pamięć tak, aby wyrównać ją do wielokrotności liczby 2, w zależności od rozmiaru kolejnego pola.
Spójrzmy na to z tej perspektywy:
struct D{
char znak; // 1 bajt
// 3 bajty wyrównawcze - wyrównanie do 4
int liczba; // 4 bajty
char znak2; // 1 bajt
// 3 bajty wyrównawcze - wyrównanie do 4
};
Łącznie 12 bajtów, sporo jak na 2 chary i inta. Pamiętajmy także, że ostatnie pole struktury jest zawsze wyrównywane tak, aby całkowity rozmiar struktury był wielokrotnością najszerszego pola w strukturze.
struct E{
char znak; // 1 bajt
char znak2; // 1 bajt
// 2 bajty wyrównawcze - wyrównanie do 4
int liczba; // 4 bajty
};
Tutaj mamy łącznie 8 bajtów. Przy utworzeniu 1000 obiektów uzyskalibyśmy dodatkowo kilka kB. Widać więc, że warto zwracać uwagę na kolejność zmiennych w strukturze np. przy programowaniu mikroprocesorów, gdzie pamięć jest często bardzo ograniczona.
Brak komentarzy:
Prześlij komentarz