next up previous contents
Elõre: Általános mutatók Fel: Mutatók és tömbök Vissza: Tömbök használata. Többdimenziós tömbök

Kapcsolat tömbök és mutatók között

A következõkben a pointerek és tömbök közötti szoros kapcsolatot mutatjuk be. Vegyük elõ az egyik fenti példát újra, kicsit más formában:

            float vekt[20], *pv = &vekt[0];
Az aritmetikai szabályok figyelembevételével ezek után a következõ párosításokat írhatjuk fel:
            *(pv+0)      vekt[0]
            *(pv+1)      vekt[1]
            *(pv+2)      vekt[2]
ahol tehát az egyes párosok mindkét tagja ugyanarra az tárolási egységre hivatkozik. Ha most egy pillanatra elfelejtjük, hogy pv mutató, akkor a fenti analógia a következõképpen tehetõ teljessé:
             pv[0]      vekt[0]
             pv[1]      vekt[1]
             pv[2]      vekt[2]
Ez összhangban van azzal, hogy a tömbbeli indexek az elemeket éppúgy sorszámozzák meg, mint ahogy a pointer aritmetikánál a memória felosztását elképzeltük, azaz egy elõrelépés az indexben egy adattal való elõrehaladást eredményez a memóriában. A vekt tömb tehát a pv mellé elképzelt memóriastruktúrában helyezkedik el, azaz a kettõt mintegy egymásra fektettük. A fenti analógia érdekében a C nyelv megengedi azt, hogy a pointereket indexelhessük a fent leírt módon, precízen megfogalmazva, a

        pointer[egész]

alakú kifejezést a fordító

        *(pointer + egész)

értelemben alkalmazza. Azért, hogy az indexkifejezés értelmezése független legyen attól hogy tömbre vagy mutatóra alkalmazzuk, a vekt[0] értelmezése is legyen *(vekt + 0), azaz *vekt, tehát vekt önmagában a vekt[ ] tömb elsõ elemére mutató pointer: vekt == &vekt[0]. A kifejezésekben bárhol elõforduló tömbazonosítót a fordító azonnal átalakítja a tömb elsõ elemét megcímzõ mutatóvá, és ha indexkifejezés követi, akkor azt a pointerek indexelésének szabályai szerint értelmezi. (Innen is látszik, hogy ilyen összefüggésben a [ ] tényleg az indexelõ operátor.) A fenti strlen()-t alkalmazó példákat is írhattuk volna - és a késõbbiek folyamán hasonló esetekben írjuk is - a következõképpen:

            l1 = strlen(hiba);
            l2 = strlen(hiba + 6);
Ebbõl viszont jól látszik az, hogy egydimenziós tömböt függvénynek paraméterként tulajdonképpen nem is lehet átadni, csak egy olyan mutatót adhatunk át, amely az adott tömb elsõ (vagy bármelyik) elemére mutat. Hasonlóan, ha van a memóriában egy elegendõen nagy terület, és arra mutat egy pointer, akkor az adott pointert úgy tekinthetjük, mintha tömb volna. Ez a mutatók és tömbök közti szoros kapcsolat teszi lehetõvé, hogy egydimenziós tömböket dinamikus indexhatárral kezelhessünk, azaz a méretüket elegendõ futási idõben rögzíteni. Például a fenti vekt használható a következõ formában is:
            float *vekt, *seged;
            ...
            vekt = (float *)malloc(n * sizeof(float));
ahol n értékét elég futási idõben meghatározni. Az így definiált vekt és az eredeti változat használata között semmi különbség nincs, csak az indexhatár más. A következõ ciklus például kinullázza a tömböt:
            int i, n = MERET;
            ...
            for (i = 0; i < n; i++)
            {
               vekt[i] = 0.0;
            } /* endfor */
A fenti példával teljesen ekvivalens az alábbi:
            for (i = 0, seged = vekt; i < n; i++, seged++)
            {
               *seged = 0.0;
            } /* endfor */
A különbség a két példa közt mindössze annyi, hogy a seged pointer a for ciklus inicializáló részében vekt-bõl megkapja a valós tömbünk báziscímét, a ciklusmagban mindig a seged által aktuálisan megcímzett memóriaterületre írjuk be a nullát, és seged-et a for ciklus léptetõ utasítás-részében inkrementáljuk. Az i ciklusváltozó ebben a megoldásban csak számlálóként szerepel.

Tömbök dinamikus kezelésére egy másik példaként írjunk olyan függvényt, amelyik visszaadja a paraméterként kapott tömb maximális elemét:

            double maximum(tomb, m)
            float    tomb[ ];
            register m;
            {    
               float    max;
               register i;

               max = tomb[0];
               for (i = 1; i < m; i++)
               {   
                  if (tomb[i] > max)
                  {
                     max = tomb[i];
                  } /* endif */
               } /* endfor */
               return max;
            } /* end maximum() */
Figyeljük meg a tomb paraméter deklarációját, ez egyenértékû a float *tomb; alakkal, de érzékelteti, hogy a pointert tömbként kívánjuk használni. A függvény meghívása például a következõ formában történhet:
            m = maximum(vekt, n);
Mivel a pointermûveletek gyorsabbak az indexeléseknél - ez utóbbiak gépi szinten szorzást is igényelnek - a fenti függvényt a gyakorlatban a következõképpen írnánk meg:
            double maximum(tomb, m)
            register float *tomb;
            int             m;
            {
               float  max;
               register float *veg = tomb + m;

               max = *tomb;
               while (++tomb < veg)
               {
                  if (*tomb > max)
                  {
                     max = *tomb;
                  } /* endif */
               } /* endw */
               return max;
            } /* end maximum() */

next up previous contents
Elõre: Általános mutatók Fel: Mutatók és tömbök Vissza: Tömbök használata. Többdimenziós tömbök