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) {
void tipmat_2(int **t, int
n, int m) {
void tipmat_3(pt3 t,int n)
{
void 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:
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".