Tratarea Tablourilor  Bidimensionale Ca Pointeri


Fie urmatoarea secventa de program:
 

//. . .
typedef int t3[3];      //t3 este numele tipului "tablou de 3 int-uri"
typedef t3 *pt3;        //pt3 este numele tipului "pointer la t3"

void tipmat_1(int **t,int n,int m) {
          //tipareste elementele t[ i ][ j ], i=1..n, j=1..m
}

void tipmat_2(int **t, int n, int m) {
          //tipareste elementele ((int *)t)[i*m+j], i=1..n, j=1..m
}

void tipmat_3(pt3 t,int n) {
          //tipareste elementele t[i][j], i=1..n, j=1..3
}

void o_functie( ) {
          int a[2][3],  //a este o matrice de 2x3 alocata in stiva
              i,j,k;
          int *r=new int[2*3];  //r indica un tablou unidimens cu 6 elemente,
                                        //alocat in heap
          t3 s[2];      //s este un tablou de 2 elemente de tip t3, alocat
                        //in stiva; de fapt declaratia lui s este echivalenta
                        //cu cea a lui 'a'
          pt3 t=new t3[2]; //t indica un tablou de 2 elemente de tip t3 alocat
                           //in heap
          pt3 w[2];     //w este un tablou de 2 pointeri la t3, alocat in
                        //stiva
          pt3 *v=new pt3[2]; //v indica un tablou de 2 pointeri la t3, alocat
                             //in heap
          int *p[2];    //p este un tablou de 2 pointeri la int, alocat in
                        //stiva
          int **q=new int*[2];  //q indica un tablou de 2 pointeri la int,
                                //alocat in heap
          for(i=0;i<2;i++)
          {
            w[i]=new t3[1];
            v[i]=new t3[1];
            p[i]=new int[3];
            q[i]=new int[3];
            for(j=0;j<3;j++)
            {
              k=3*i+j; //formula de calcul a pozitiei unui element de indici
                       //i si j, in cazul reprezentarii liniare a unei matrici
                       //in memorie
              a[ i ][ j ]=k;
              s[ i ][ j ]=k;
              t[ i ][ j ]=k;
              ((int *)w[ i ])[ j ]=k;
              ((int *)v[ i ])[ j ]=k;
              p[ i ][ j ]=k;
              q[ i ][ j ]=k;
              r[k]=k;
            }//end for-j
          }//end for-i
          //apeluri la functiile tipmat_i
        tipmat_1(q,2,3);
        tipmat_1(p,2,3);
        tipmat_1((int**)v,2,3);
        tipmat_1((int **)w,2,3);
        tipmat_2((int **)a,2,3);
        tipmat_2((int **)t,2,3);
        tipmat_2((int **)s,2,3);
        tipmat_2((int **)r,2,3);
        tipmat_3(a,2);
        tipmat_3(s,2);
        tipmat_3(t,2);
        tipmat_3((pt3)r,2);
          //eliberarea memoriei alocate cu new
          for(i=0;i<2;i++)
          {
            delete w[i]; delete v[i];
            delete p[i]; delete q[i];
          }//end for-i
          delete r; delete t; delete v; delete q;
}//end o_functie 

In aceasta secventa sunt prezentate diverse posibilitati de creare a tablourilor bidimensionale. Diferentele dintre ele rezida in principal in modul de dispunere a elementelor in memorie. Practic, in afara de r, tablourile sunt doua cate doua "asemenea" ca mod de utilizare, unul fiind insa alocat in stiva iar celalalt in heap; astfel avem: a<=>t, w<=>v si p<=>q. In interiorul functiei in care au fost declarate tablourile se observa ca accesul la un element se face folosind notatia nume_tab[i][j] pentru toate variantele, exceptandu-l pe r (in cazul lui r este vorba de o reprezentare explicit liniara pentru o matrice si e sarcina programatorului sa determine pozitia unui element pe baza a 2 indici). In cazul variabilelor v si w se poate observa ca a fost necesar un casting (int *), deoarece w[i] si v[i] sunt de fapt pointeri la t3; ca urmare, w[i][j], respectiv v[i][j] ar reprezenta un bloc de 3 int-uri si nu un int.
Posibilitatea utilizarii unei notatii unice, desi variabilele respective sunt de tipuri diferite, decurge din modul in care este interpretata de compilator o referire de forma t[i][j], si anume:

t[i][j] <=> *(*(t+i)+j)
Propunem cititorului sa aplice aceasta interpretare la fiecare dintre variabilele declarate (mai putin pentru r) pentru o pereche (i,j) aleasa. Se face observatia ca in cazul lui a, care este un tablou de tablouri de 3 int-uri, tipul sau este considerat a fi echivalent cu pt3.

Diferenta de tip si de dispunere a elementelor in memorie devine o problema cand tablourile respective trebuie transmise ca parametri unei functii, de exemplu in vederea afisarii. In exemplul prezentat sunt date 3 variante ale unei functii de afisare, care difera intre ele prin tipul parametrului corespunzator matricii si/sau prin modul de referire a unui element.
In varianta 2 functia este "constienta" ca elementele matricii formeaza o secventa liniara in memorie. Celelalte variante "mizeaza" pe faptul ca fiecare linie de cate m elemente este stocata separat, fiind indicata de cate un pointer care exista si el fizic in memorie. Facem observatia ca varianta 3 "pacatuieste" prin lipsa de generalitate. Cu aceste precizari vom arata in continuare cum trebuie apelate functiile tipmat, astfel incat ele sa opereze corect.
 

        tipmat_1(q,2,3);
        tipmat_1(p,2,3);
        tipmat_1((int**)v,2,3);
        tipmat_1((int **)w,2,3);
        tipmat_2((int **)a,2,3);
        tipmat_2((int **)t,2,3);
        tipmat_2((int **)s,2,3);
        tipmat_2((int **)r,2,3);
        tipmat_3(a,2);
        tipmat_3(s,2);
        tipmat_3(t,2);
        tipmat_3((pt3)r,2);

Orice alt apel va conduce la comportari eronate ale functiilor tipmat, soldate cel mai adesea cu "Segmetation fault". Ca exemplu, vom detalia cazul
                tipmat_1((int **)a,2,3);
inlocuind in functia tipmat_1 parametrul t (de tip int**) cu a (care este de fapt de tip pt3) si interpretand notatia t[i][j] pentru cazul (ideal!) i=0, avem
        a+i <=> a + i*sizeof(int**) = a
        *(a) <=> valoarea citita de la adresa a, pe sizeof(int**) octeti!;
aceasta valoare se va interpreta ca o adresa la int, cand de fapt este, in cel mai bun caz, un numar intreg;
        *(a)+j <=> *(a) + j*sizeof(int)
        *(*(a)+j) <=> valoarea citita de la adresa calculata mai sus, pe sizeof(int) octeti; este foarte probabil ca incercarea de a accesa o asemenea adresa se va solda cu "Segmentation fault".
 
 

Hosted by www.Geocities.ws

1