Smerníky alebo "lovenie v pamäti".

Smerníky sú "duša a srdce" jazyka C. Iba ten, kto im rozumie a vie ich využívať si môže dovoliť tvrdiť, že jazyk C ovláda.

Smerník je premenná, do ktorej sa neukladá číslo ale adresa v pamäti. Smerník je ako šipka, ktorá niekam do pamäte ukazuje. Okrem toho, kam smerník ukazuje, mu väčšinou treba povedať aj to, aký typ premennej tam môže očakávať. Takže sú smerníky, ktoré sú typu int pretože ukazujú na premenné typu int, sú smerníky typu float, typu char atď.

Ako to funguje? Pozrite si nasledovný príklad. Nech funkcia main() vyzerá takto:

    int i;
    int *pi;
    i = 5;
    pi = &i;

Prvý a druhý riadok sa líšia len nepatrne -- jedinou hviezdičkou. V pvom riadku je deklarovaná premenná i typu int. Pri deklarácii sa premennej vyhradí v pamäti miesto (veľké 4 bajty). Druhý riadok vygeneruje smerník. Do premennej pi (z anglického "pointer to integer" -- smerník na celé číslo. 0.14) môžeme uložiť adresu v pamäti, na ktorej sa nachádza nejaký int. Môžeme do neho uložiť napríklad adresu, na ktorej sa nachádza premenná i. To sa skutočne udeje v poslednom riadku. Po vykonaní tejto sekvencie je situácia asi takáto:

\psfig{file=smernik.eps,width=10cm}

V premennej pi je teda uložená pozícia v pamäti bffff7f40.15 na ktorej sa nachádza premenná i.

So smerníkmi sme sa už dvakrát stretli, aj keď sme ich vyslovene nespomínali. Prvýkrát, keď sme používali funkciu scanf, ktorá potrebuje poznať adresu v pamäti, kam má načítať hodnotu. Výraz &i, ktorý v takej situácii používame je vlastne smerník ukazujúci na premennú i. Druhýkrát sme sa so smerníkmi stretli, keď sme sa učili pracovať s poľami. Zápis FILE *f; ktorý sme používali, deklaroval smerník na šruktúru typu FILE.

Ak poznáme premennú, adresu na ktorej sa nachádza vieme zistiť. Slúži na to znak & pred menom premennej. Čo ale robiť v prípade, keď je situácia opačná? Predstavte si, že z kusu programu uvedeného hore zavoláme nejakú funkciu, ktorej odovzdáme iba hodnotu pi. Môže si táto funkcia zistiť, akú hodnotu má premenná i aj keď o jej existencii vôbec nevie a pozná iba jej adresu? Odpoveď je áno. Slúži na to znak *. Ak by sme teda napísali printf("%d\n",*pi); na obrazovku by sa vypísalo 5. Mimochodom deklarácia int *pi; sa dá čítať dvoma rôznymi spôsobmi: "pi je smerník na int" alebo "*pi je celé číslo". S výrazom *pi sa dá pracovať ako s obyčajnou premennou. Môžem napríklad napísať *pi = 7; a do miesta v pamäti kam ukazuje smerník pi sa vloží číslo 7.

Nuž, rečí o smerníkoch bolo veľa ale zatiaľ sme ešte nepovedali, na čo sú také smerníky vlastne dobré. Na veľa vecí. Dobre sa s ich pomocou manipuluje s veľkými štruktúrami. Napríklad štruktúra FILE zaberá (v gcc pod linuxom) 148 bajtov. A je oveľa jednoduchšie pracovať so smerníkom na ňu (ktorý má 4 bajty) než s celým tým hebedom. S pomocou smerníkov sa dá priamo pristupovať do pamäte. O ďalších výhodách (napr. možnosť dynamického prideľovania pamäte) si povieme neskôr. Teraz si pozrite nasledujúci príklad. Máme tam dve funkcie swap1 a swap2, ktorých úlohou je vymeniť hodnoty v dvoch premenných typu int.

    #include<stdio.h>
    
    void swap1(int a, int b)
    {
    	int c;

        c = a;
        a = b;
        b = c;
    }
    
    void swap2(int *pa, int *pb)
    {
    	int c;

        c = *pa;
        *pa = *pb;
        *pb = c;
    }
    
    main()
    {
        int i = 3, j = 5;
        swap1(i,j);
        printf("i = %d, j = %d\n",i,j);
        swap2(&i,&j);
        printf("i = %d, j = %d\n",i,j);
    }

Úloha č.1 Napíšte, skompilujte, pozrite, čo to robí.

Vyzerá to tak, že funkcia swap1 hodnoty neprehodí, zatiaľ čo funkcia swap2 áno. Prečo je to tak? Dôvod je jednoduchý: každá funkcia má svoje vlastné premenné s ktorými vie robiť a do súkromných premenných ostatných funkcií nevidí. Takže keď sa volá prvá funkcia, do jej súkromných premenných a a b sa zapíšu hodnoty z i a j, potom sa obsah premenných a a b medzi sebou vymení, ale premenné i a j to už nijako nezasiahne. Funkcii swap2 však prezradíme, kde sa premenné i a j nachádzaju v pamäti. Adresa premennej i sa uloží do pa a adresa premennej j do pb. Funkcia swap2 siahne do pamäte na určené miesta a hodnoty, ktoré tam nájde vymení.

(Predošlý odstavec a program povyše si čítajte až dovtedy, kým to nepochopíte. Je to dôležité.)

A na záver -- ako vždy -- úlohy:



Úloha č.2 Napíšte procedúru dva, ktorá dostane na vstupe smerník na int a na to miesto v pamäti, kam smerník ukazuje, vloží číslo 2. Napíšte hlavný program, v ktorom takúto funkciu použijete.

Úloha č.3 Napíšte procedúru plus, ktorá dostane na vstupe smerník na int a premennú, na ktorú smerník ukazuje, zväčší o 1. Použite v hlavnom programe.

Úloha č.4 Napíšte procedúru sucet(int a, int b, int *vys) ktorá do premennej na ktorú ukazuje vys vloží súčet a + b.

Dobrá rada: Nemusíte to celé zakaždým pchať do nového súboru. Tri funkcie a main sa v jednom súbore znesú, zavolajte z mainu všetky tri funkcie a ušetríte si robotu.

Anino Belan 2003-10-26