Zauważyłem coś dziwnego po skompilowaniu tego kodu na moim komputerze:
#include <stdio.h>
int main()
{
printf("Hello, World!\n");
int a,b,c,d;
int e,f,g;
long int h;
printf("The addresses are:\n %0x \n %0x \n %0x \n %0x \n %0x \n %0x \n %0x \n %0x",
&a,&b,&c,&d,&e,&f,&g,&h);
return 0;
}
Wynik jest następujący. Zauważ, że między każdym adresem int występuje 4-bajtowa różnica. Jednak między ostatnim intem a długim intem istnieje 12-bajtowa różnica:
Hello, World!
The addresses are:
da54dcac
da54dca8
da54dca4
da54dca0
da54dc9c
da54dc98
da54dc94
da54dc88
int
poh
w kodzie źródłowym. Kompilator może wcześniej wstawić go do lukih
.sizeof
funkcja.printf("size: %d ", sizeof(long));
%x
. Na szczęście dla ciebie, zdarza się, że działa poprawnie na twojej platformie, aby przekazywać argumenty wskaźnikowe z oczekiwanym ciągiem formatuunsigned int
, ale wskaźniki i liczby całkowite mają różne rozmiary w wielu ABI. Służy%p
do drukowania wskaźników w przenośnym kodzie. (Łatwo jest wyobrazić sobie system, w którym kod drukowałby górną / dolną połowę pierwszych 4 wskaźników zamiast dolnej połowy wszystkich 8.)%zu
. @yoyo_fun do drukowania adresów użyj%p
. Użycie niewłaściwego specyfikatora formatu wywołuje niezdefiniowane zachowanieOdpowiedzi:
Nie zajęło 12 bajtów, zajęło tylko 8. Jednak domyślne wyrównanie 8-bajtowego int na tej platformie to 8 bajtów. W związku z tym kompilator musiał przenieść long int na adres, który jest podzielny przez 8. Adres „oczywisty”, da54dc8c, nie jest podzielny przez 8, stąd 12-bajtowa przerwa.
Powinieneś być w stanie to przetestować. Jeśli dodasz kolejny int przed długim, więc jest ich 8, powinieneś zauważyć, że long int zostanie wyrównany bez ruchu. Teraz będzie tylko 8 bajtów z poprzedniego adresu.
Prawdopodobnie warto zauważyć, że chociaż ten test powinien działać, nie należy polegać na tak zorganizowanych zmiennych. Kompilator prądu przemiennego może wykonywać różne czynności, aby przyspieszyć działanie programu, w tym zmieniać kolejność zmiennych (z pewnymi zastrzeżeniami).
źródło
Wynika to z faktu, że kompilator generuje dodatkowe wypełnienie między zmiennymi, aby zapewnić ich prawidłowe wyrównanie w pamięci.
W większości nowoczesnych procesorów, jeśli wartość ma adres wielokrotności jej wielkości, bardziej efektywny jest dostęp do niej. Gdyby umieścił
h
w pierwszym dostępnym miejscu, jego adres to 0xda54dc8c, który nie jest wielokrotnością liczby 8, więc użycie go byłoby mniej wydajne. Kompilator wie o tym i dodaje trochę nieużywanego miejsca między dwiema ostatnimi zmiennymi, aby się upewnić.źródło
Twój test niekoniecznie testuje to, co myślisz, ponieważ nie ma wymogu, aby język odnosił między sobą adres którejkolwiek z tych zmiennych lokalnych.
Musisz umieścić je jako pola w strukturze, aby móc wywnioskować coś o alokacji pamięci.
Zmienne lokalne nie są wymagane do dzielenia pamięci obok siebie w żaden szczególny sposób. Kompilator może wstawić zmienną tymczasową w dowolnym miejscu stosu, na przykład, która może znajdować się pomiędzy dowolnymi dwoma z tych zmiennych lokalnych.
W przeciwieństwie do tego, nie byłoby dozwolone wstawianie zmiennej tymczasowej do struktury, więc jeśli zamiast tego wydrukowałbyś adresy pól struct, porównywałbyś zamierzone elementy przydzielone z tego samego logicznego uchwytu pamięci (struktury).
źródło