Prin locatie de memorie vom intelege, in continuare, cea mai mica unitate de memorie adresabila. De obicei, unitatea minima de memorie adresabila este byte-ul. De obicei un byte este format din 8 biti (un bit este unitatea minima de reprezentare a datelor), caz in care se vorbeste despre octet.
Locatiile de memorie sunt consecutive si numarul lor de ordine (numar intreg pozitiv!) reprezinta adresa lor . Exista o stransa legatura intre dimensiunea memoriei adresabile si numaru 333c25d l de biti pe care se reprezinta adresele locatiilor de memorie. De ex. cu adrese pe 2 octeti (16 biti) se poate adresa un spatiu de memorie de maximum 216=65536 locatii (64KB). Cu adrese pe 4 octeti se poate adresa un spatiu de memorie de 232=4294967296 locatii (4GB).
Informatiile memorate de un calculator (fie ele date sau coduri de instructiuni sau chiar adrese) pot ocupa una sau mai multe locatii succesive de memorie. Indiferent insa cate locatii de memorie ocupa o informatie ce trebuie stocata, adresa de inceput a grupului de locatii ocupate este.o adresa. Altfel spus, informatiile de tip adresa ocupa acelasi numar de octeti, indiferent de tipul informatiei stocate incepand cu acea adresa. De obicei adresele se reprezinta pe doi sau patru octeti. Adresele reprezentate pe doi octeti sunt adrese relative in cadrul unor segmente de memorie de 64KB, si sunt utilizate de obicei la calculatoare cu registri pe 16 biti, necesitand o schema de prelucrare care sa permita accesarea unui spatiu de memorie mai mare decat 64KB.
In exemplul de mai jos, adresele (de inceput ale) celor trei date sunt cele cu font ingrosat. Data de tip long int ocupa 4 bytes (de la 0100 la 0103, inclusiv), data de tip char ocupa un byte (la adresa 0106) iar data de tip int ocupa 2 bytes (de la adresa 010A la 010B, inclusiv).
0100 0101
010A
010B
}long int (4 bytes)
} char (1 byte)
}int (2 bytes)
Definitie1. Pointerii sunt variabile a caror valoare este interpretata ca adresa a unei locatii de memorie.
Spatiul ocupat de o variabila pointer este raportat corect de operatorul sizeof
Sintaxa declaratiei unei variabile pointer este:
TIP *nume_variabila;
unde TIP este orice tip de data fundamental sau definit de catre programator.
Definitie2. Pointerii sunt variabile a caror valoare este interpretata ca adresa a unei locatii de memorie unde poate fi accesata o valoare de tipul declarat al pointerului.
La fel ca in cazul oricaror variabile, variabilele pointer globale sau cele locale declarate static se initializeaza automat, cu valoarea NULL. NULL, ca valoare a unui pointer, este o adresa invalida! Ea se foloseste doar pentru a indica faptul ca pointerul care are valoarea respectiva nu poate fi utilizat intr-o operatie de dereferentiere.
Operatorul unar & se uilizeaza in expresii a caror valoare reprezinta adresa variabilei operand.
Ex.
TIP var, *pvar; /* var - variabila de tipul TIP pvar - variabila "pointer la variabile de tipul TIP
pvar = &var; valoarea lui pvar reprezinta adresa variabilei var
OBS. TIP poate fi oricare din tipurile fundamentale sau definite de catre utilizator (inclusiv pointer!)
Operatorul unar se utilizeaza in expresii a caror valoare reprezinta valoarea memorata la adresa indicata de operand (variabila pointer).
Ex:
TIP var=expresie,*pvar; /* var - variabila de tipul TIP; pvar - variabila "pointer la variabile de tipul TIP" */
pvar = &var; valoarea lui pvar reprezinta adresa varibilei var */
*pvar /* *pvar este o valoare de tipul TIP egala cu valoarea espresiei expresie
Daca tipul declarat al unui pointer este void *, aceasta semnifica absenta oricarei informatii despre tipul valorii memorate incepand cu adresa care reprezinta valoarea pointerului. Astfel de pointeri nu pot fi folositi decat pentru memorarea unor adrese nu si pentreu accesul la datele de la adresele respective!
Asupra pointerilor de tip void * nu se pot face nici un fel de operatii (din cele admise asupra pointerilor) cu exceptia asignarii unui astfel de pointer la alt pointer sau al asignarii unui pointer la un astfel de pointer.
Ex:
int v, *pi=&v;
char *pc;
void *p;
p=pi; /*corect! p contine adresa variabilei v */
*p. /* eronat! p nu poate fi dereferenetiat pt ca nu se cunoaste tipul datei de al adresa p
pc=p: /* corect! pc contine adresa primului octet (un char) de la adresa din p=pi deci
primul octet al lui v
Argumentele formale declarate ca tablouri sunt pointeri! Intr-adevar, daca un argument formal al unei functii este un tablou, la apelul functiei respective ceea ce se transmite este adresa tabloului. In functia apelata, argumentul respectiv este insa o variabila al carei continut este o adresa. Dar, variabilele al caror continut (valoare) reperzinta o adresa sunt (se numesc).pointeri!
OBS. De altfel, la apelul unei astfel de functii, argumentul actual corespunzator argumentului formal declarat ca tablou este, de obicei, numele unui tablou. Numele unui tablou este sinonim cu adresa de inceput a tabloului, Q.E.D.!
Operatii legale asupra pointerilor:
Asignarea unei adrese la un pointer
Atribuirea unui pointer la alt pointer
Dereferentierea unui pointer (daca e de alt tip decat void *!)
Adunarea/scaderea unei constante la/dintr- un pointer (atunci cand pointerul indica spre un element al unui tablou)
Incrementarea/decrementarea unui pointer (atunci cand pointerul indica spre un element al unui tablou)
Scaderea unui pointer din alt pointer (atunci cand ambii indica spre elemente ale aceluiasi tablou)
Compararea a doi pointeri care indica spre elemente ale aceluiasi tablou
Operatii ilegale
Oricare din operatiile care nu sunt legale! ;-)
Exista o stransa legatura intre pointeri si tablouri. De obicei (desi nu in exclusivitate!), pointerii se utilizeaza in conjunctie cu tablourile (pentru a accesa elemente ale unui tablou). Cu o frecventa si mai mare se utilizeaza pointerii pentru prelucrarea sirurilor de caractere (tablouri - declarate ca atare sau nu! - al caror continut este o succesiune de caractere din care ultimul are valoarea '\0' , asa numitul caracter terminator de sir). Toate functiile de tratare a sirurilor de caractere (care au prototipul in headerul string,h) reprezinta exemple de prelucrare prin intermediul pointerilor.
Tinand cont de faptul ca numele unui tablou reprezinta un sinonim pentru adresa unui tablou, se poate utiliza numele unui tablou ca pointer.
Ex.
TIP t[DIM], *p=t;
t[i este tot una cu *(t+i) /* t+i reprezinta adresa celui de-al i+1 -lea element al tabloului t */
Pe de alta parte, intrucat evaluarea oricarei expresii reprezentand un element de tablou presupune un calcul de adresa, un pointer poate fi utilizat ca nume de tablou.
OBS. Echivalenta dintre numele de tablouri si pointeri se opreste insa acolo unde e vorba de operatii care ar avea ca urmare modificarea adresei.
Ex.
p++;
p=p+i;
sunt ambele expresii corecte, pe cand
t++;
t=t+i;
sunt ambele eronate, intrucat presupun modificarea lui t care este o constanta (adresa de memorie de la care incepand sunt memorate elementele tabloului!
Pointerii, desi contribuie la scaderea lizibilitatii programelor C reprezinta in anumite situatii o alternativa mult mai eficienta decat utilizarea tablourilor. Pe de o parte utilizarea pointerilor poate micsora semnificativ timpul de acces la elementele unui tablou iar pe de alta parte, utilizarea pointerilor reprezinta singura modalitate de acces la zone de memorie alocate dinamic (in timpul executiei).
Ex.
for(i=0; i < DIM ; i++) /* la fiecare iteratie: o comparatie si o incrementare */
t[i]=expresie; /*o inmultire, o adunare si o asignare (in afara de operatiile presupuse de evaluarea expresiei)*/
for(p=t,i=0;i<DIM;i++,p++) /* la fiecare iteratie: o comparatie si doua incrementari */
*p=expresie; / * o asignare (in afara de operatiile presupuse de evaluarea expresiei)*/
OBS. Diferenta: o inmultire si o adunare la varianta cu tablou fata de o incrementare la varianta cu pointer. Chiar daca presupunem ca incrementarea este echivalenta ca timp de executie cu adunarea (in nici un caz nu este mai lunga !) in versiunea in care se foloseste pointerl se economiseste timpul necesar unei inmultiri la fiecare ciclu ! Daca in expresie mai apar si alte referiri la elemente ale tabloului diferenta creste.
Desi functiile nu sunt variabile, ele ocupa totusi o zona de memorie, cu alte cuvinte, incep de la o anumita adresa! De aceea, in C se pot declara pointeri la functii, prin intermediul carora se pot apela functiile a caror adresa o contin. In mod similar numelor de tablouri, numele unei functii este sinonim cu adresa tabloului.
Ex.
int (*pf)(char *); /*pointer la functie care asteapta ca argument un char * si care intoarce un int
pf=strlen; /* initializarea pointerului cu adresa unei functii de tipul declarat al pointerului */
n=(*pf)("Timisoara"); /* apelul functiei strlen prin intermediul pointerului pf */
Spre deosebire de alte limbaje, in C un tablou multidimensional este un tablou .unidimensional ale carui elemente sunt .tablouri!
Ex.
TIP t[N][M];
In exemplul anterior, t este un tablou de N elemente, fiecare element fiind un tablou de M elemente de tipul TIP! Aceasta inseamna ca t[i] este un tablou de M elemente de tipul TIP.
La fel ca si in cazul tablourilor unidimensionale, dimensiunile (precizate in declaratia) tablourilor multidimensionale trebuie sa fie expresii constante.
In cazul in care un tablou multidimensional este transmis ca argument unei functii, prima dimensiune nu trebuie precizata in declaratia functiei, in schimb toate celelalte dimensiuni sunt obligatorii, pentru a permite compilatorului sa calculeze corect adresa unui element atunci cand se foloseste indexarea
TIP fct( TIP t[ ][N2][N3]);
Aceasta necesitate rezulta clar din modul in care o expresie desemnand un element de tablou este folosita pentru a calcula adresa acestuia:
Elementul t[i][j][k] se afla la adresa t + ( i - 1)*N2*N3 + j.
Pointerii fiind variabile, se pot agrega (grupa) in tablouri.
Declaratia unui tablou de pointeri arata ca mai jos:
TIP *ptr_tab[N];
Tablourile de pointeri reprezinta alternativa (economica) la utilizarea tablourilor bidin\mensionale, mai ales a tablourilor bidimensionale de caractere.
Ex (reprez. Grafica).
Un ui program C I se pot transmite anumite argumente (siruri de caractere) la lansarea in executie. Pentru aceasta, functia main trebuie declarata ca asteptand doua argumente:
argc - un intreg care are ca valoare numarul de argumente cu care a fost lansat in executie programul (inclusiv numele acestuia)
argv - un tablou de pointeri catre sirurile de caractere care reprezinta argumentele
Ex.
main( int argc, char *argv[ ])
|