Adresy zmiennych i rozmiary zmiennych


Aby zapoznać się z tym zagadnieniem to najlepiej napisać sobie prosty program, który pokaże informacje o zmiennych zadeklarowanych w nim.

pamiec.c

int main ( void )
{
    int a = 2;

    printf("a: adres: %X, zawartosc: %d, dlugosc: %d \n", &a, a, sizeof(a));

    return 0;
}
W wyniku otrzymamy taką oto linijkę:

    a: adres: BFFFFCC8, zawartosc: 2, dlugosc: 4
W wywołaniu funkcji printf() poszczególne zmienne zanoszone do funkcji oznaczają:

&a - adres zmiennej. %X oznacza, ze liczba będzie wyświetlona szesnastkowo
a - zawartość zmiennej. %d - liczba będzie wyświetlona dziesiętnie jako wartość całkowita
sizeof(a) - rozmiar zmiennej :) też wyświetlony dziesiętnie

Jak widać, zmienna typu int znajduje się pod adresem 0xbffffcc8, jej zawartość to 2, natomiast długość to 4. Długość oznacza tutaj ilość zajmowanych przez nią bajtów :)

Warto zwrócić uwagę, szczególnie przy większych projektach lub gdy zależy nam na szybkości obliczeń w programie, że nie zawsze potrzebujemy aż 4 Bajtów aby coś na nich przedstawić czy policzyć :) Często wystarcza 1! :)

Wprowadźcie do poprzedniego programu taką modyfikację:

int main ( void )
{
    char a = 65;

    printf("a: adres: %x, zawartosc: %d, dlugosc: %d \n", &a, a, sizeof(a));

    return 0;
}
Wynikiem będzie:
    a: adres: bffffccb, zawartosc: 65, dlugosc: 1
%x oznacza to samo co %X z tą różnicą, że liczba będzie pisana małymi literami.
Widzimy tutaj, że długość zmiennej jest mniejsza :) i wynosi tylko 1 Bajt. Tak więc dla zmiennej typu char program rezerwuje 1 Bajt.

A co z tablicami? :>
Dla utrudnienia dajmy tak:
int main ( void )
{
    char tab[] = "dupa123";

    printf("a: adres: %X, zawartosc: %s, dlugosc: %d \n", &tab, tab, sizeof(tab));

    return 0;
}
Wynik:
    a: adres: BFFFFCC4, zawartosc: dupa123, dlugosc: 8
Specjalnie deklarację tablicy dałam taką:
    char tab[] = "dupa123";
gdyż widać tu, co dzieje się z ilością zajmowanych przez string Bajtów.
Ciąg znaków jest długości 7, ale zajął 8 Bajtów. Dzieje się tak, ponieważ łańcuchy znaków rezerwują sobie o jeden Bajt więcej na znak końca stringu. Znakiem tym jest \0 :)
Jeśli chcecie się przekonać, że tak rzeczywiście jest, to musicie napisać krótki program. Polecam sprawdzanie różnych rzeczy tego typu, gdyż nie zawsze powinno ufać się na słowo :) Sprawdzajcie wszystko bo dzięki temu człowiek uczy się i lepiej przyswaja wiedzę :)





Pewnie już zauważyliście, że adresy są różne :) Jak więc te zmienne rozkładają się w pamięci?

Poniższy przykład wyjaśni sprawę :)
int main ( void )
{
    char tab[] = "dupa123";
    int i;

    for( i=0; i<8; i++)
    {
        printf("[%x] Bajt %d: %c = |%d|\n", &tab[i], i, tab[i], tab[i] );
    }

    return 0;
}
Wyświetlamy kolejne zawartości Bajtów, zaczynając od początku tablicy czyli od tab[0]. Kończymy na tab[7]. Czyli od 0 do 7 mamy razem 8 Bajtów :) Zawsze pierwszy element tablicy to tab[0] a nie tab[1]. Zapamiętajcie :)

Wynik:
    [bffffcc4] Bajt 0: d = |100|
    [bffffcc5] Bajt 1: u = |117|
    [bffffcc6] Bajt 2: p = |112|
    [bffffcc7] Bajt 3: a = |97|
    [bffffcc8] Bajt 4: 1 = |49|
    [bffffcc9] Bajt 5: 2 = |50|
    [bffffcca] Bajt 6: 3 = |51|
    [bffffccb] Bajt 7:  = |0|
Jaki z tego wniosek? :) Ostatni Bajt jak widać ma zawartość "0" :) Specjalnie wyświetliłam tu zawartość dziesiętną obok znaku, gdyż \0 jest niedrukowalne i nie byłoby widać :)

Co ciekawsze.. kolejne adresy poszczególnych zmiennych deklarowanych w funkcji main() nie są w kolejności rosnącej, ale zaczynają się od tyłu :)

Aby rozjaśnić sprawę, spójrzcie na poniższy program:
int main ( void )
{
    int a = 2;
    char b = 65;

    printf("a: adres: %x, zawartosc: %d, dlugosc: %d \n", &a, a, sizeof(a));
    printf("b: adres: %x, zawartosc: %c, dlugosc: %d \n", &b, b, sizeof(b));

    return 0;
}
Najpierw deklarujemy zmienną typu int a później znaczek char :)

Wynik jest taki:
	a: adres: bffffcc8, zawartosc: 2, dlugosc: 4
	b: adres: bffffcc7, zawartosc: A, dlugosc: 1
Pod adresem 0xbffffcc7 znajduje się znaczek zadeklarowany jako char. Ma on długość jednego Bajta. Zawiera literkę A.
Tuż za nim pod adresem 0xbffffcc8 (czyli Bajt dalej) znajduje się kolejna zmienna typu int :) Ma ona długość czterech Bajtów i zawiera liczbę 2 :)

Im więcej zmiennych w programie tym bardziej "do przodu" pamięci przesuwamy się.

Zmienne tutaj przedstawione były zmiennymi lokalnymi dla funkcji main(). A jakie adresy dostają zmienne globalne? :)
Koniecznie sprawdzamy bo może okazać się coś innego :)
int a = 2;
int b = 3;
int c = 4;
char aa = 65;
char bb = 66;
char cc = 67;

int main ( void )
{
    printf("a: adres: %x, zawartosc: %d, dlugosc: %d \n", &a, a, sizeof(a));
    printf("b: adres: %x, zawartosc: %d, dlugosc: %d \n", &b, b, sizeof(b));
    printf("c: adres: %x, zawartosc: %d, dlugosc: %d \n", &c, c, sizeof(c));
    printf("aa: adres: %x, zawartosc: %c, dlugosc: %d \n", &aa, aa, sizeof(aa));
    printf("bb: adres: %x, zawartosc: %c, dlugosc: %d \n", &bb, bb, sizeof(bb));
    printf("cc: adres: %x, zawartosc: %c, dlugosc: %d n", &cc, cc, sizeof(cc));

    return 0;
}
Specjalnie napisałam tyle linijek aby lepiej było widać :)

Wynik:
a: adres: 804969c, zawartosc: 2, dlugosc: 4
b: adres: 80496a0, zawartosc: 3, dlugosc: 4
c: adres: 80496a4, zawartosc: 4, dlugosc: 4
aa: adres: 80496a8, zawartosc: A, dlugosc: 1
bb: adres: 80496a9, zawartosc: B, dlugosc: 1
cc: adres: 80496aa, zawartosc: C, dlugosc: 1
Ups..

Zmienna "a" jest typu int i znajduje się pod adresem 0x804969c. Ma 4B.
Ale kolejna zmienna "b" znajduje się dokładnie 4 Bajty dalej, gdyż 0x804969c + 4 = 0x80496a0
To samo "c" :)
Zmienna "aa" jest pod 80496a8 ( 80496a4 + 4 ).
Zmienna "bb" jest pod 80496a9 ( 80496a8 + 1 ), gdyż zmienna będąca przed nią miała 1 Bajt :)
To samo dotyczy zmiennej "cc".

Ups..

No cóż :) Nic, tylko sprawdzać :)

Posprawdzajcie teraz ilości Bajtów wykorzystywane przez inne zmienne :)
Dla przykładu double, long int, short int itp :)

Życzę miłej zabawy z adresami zmiennych :)

Neutrinka
www.kwant.info