Documente Academic
Documente Profesional
Documente Cultură
Tipuri Dinamice de Date. Pointeri
Tipuri Dinamice de Date. Pointeri
Pointeri
Pointerul este un tip de dat predefinit, care are ca valoare adresa unei zone de memorie (figura 1.1).
Memoria intern Segment:offset Pointer Zona de memorie indicat de pointer
Figura 1.1 Un pointer este adresa unei alte zone de memorie Folosirea pointerilor prezint urmtoarele avantaje: nlocuirea expresiilor cu indici nmulirile din formula de calcul al rangului se transform n adunri i deplasri; posibilitatea alocrii dinamice a memoriei; folosirea tipurilor procedurale de date; calculul adreselor. n operaiile cu pointeri se folosesc urmtorii operatori specifici: Operatori Simbol Utilizare Operator de refereniere * tip* Operator de refereniere & &nume Operator de derefereniere * *nume * definete un nou tip de dat (pointer la tip); & extrage adresa unei variabile (creeaz o referin); * acceseaz coninutul zonei de memorie indicate de pointer. Cei doi operatori au efect invers: *&nume nume. Exemplu: *&nume reprezint valoarea de la adresa variabilei nume (valoarea variabilei nume).
ambele atribuiri sunt corecte; nume poate primi ca valoare adresa oricrei variabile, de orice tip. Iniializarea pointerilor se poate realiza ca n exemplul precedent sau, ca i pentru celelalte variabile, la declarare, astfel:
int a; int* nume=&a;
Se observ folosirea operatorului de refereniere & pentru a crea o referin ctre variabila a. La alocarea dinamic a memoriei se folosete o alt metod pentru iniializarea unui pointer. Operatorul de derefereniere se utilizeaz att
Programarea calculatoarelor
pentru definirea tipului pointer, ct i pentru referirea datelor de la adresa indicat de pointer. Exemplu:
int a,b,c; int* nume; void* nume2; b=5; nume=&a; *nume=b; c=*nume+b; nume2=&b; *(int*)nume2=10; c=*(int*)nume2;
Se observ folosirea conversiei de tip (typecasting), atunci cnd se lucreaz cu pointeri spre tipul void (fr tip). Chiar dac un pointer spre tipul void poate primi ca valoare adresa unei variabile de orice tip, pentru a putea lucra cu ea este necesar gestionarea corect a tipului operanzilor.
Incrementare/decrementare Dac nume este pointer spre un tip TIP, prin incrementare/decrementare, valoarea lui nume se incrementeaz/decrementeaz cu numrul de octei necesari pentru a memora o dat de tip TIP, adic cu sizeof(TIP).
nume++ nume are ca valoare o adres care este incrementat i primete valoarea nume+sizeof(int) (care este adresa lui b); nume2-nume are ca valoare o adres care este decrementat i primete valoarea nume-sizeof(int) (care este adresa lui c);
nume 4B
nume2 4B
a 2B 2B
b 2B
n urma atribuirii ++p sau p++, p va avea ca valoare adresa lui v[i] plus 4 octei, adic adresa lui v[i+1]. Adunarea/scderea unui ntreg n general, dac p este un pointer spre un tip TIP, atunci cnd se adun un ntreg n la pointerul p, rezultatul va fi tot un pointer spre TIP, care are ca valoare adresa memorat n p, la care se adun de n ori numrul de octei necesari pentru a memora o dat de tip TIP, adic n*sizeof(TIP). Asemntor se execut scderea unui ntreg dintr-un pointer. nume+n nume primete valoarea nume+n*sizeof(int) nume-n nume primete valoarea nume-n*sizeof(int) Exemplu: Fie p i q pointeri spre tipul float (float* p, *q). Presupunnd c p a fost iniializat cu valoarea 0x0fff:0x3450, n urma operaiei q=p+3, q primete valoarea 0xfff:0x345c (se adun 3*4 octei). n urma operaiei q=p-2, q primete valoarea 0xffff:0x344a (se scad 2*4 octei). Operaiile descrise anterior se folosesc frecvent n lucrul cu masive. Compararea a doi pointeri Limbajul C permite compararea a doi pointeri ntr-o expresie, folosind oricare din operatorii relaionali (==, !=, <, >, <=, >=). Rezultatul expresiei nume op nume2 (unde op este unul din operatorii precizai anterior) este adevrat (nenul) sau fals (zero) dup cum nume este egal, mai mare sau mai mic dect nume2. Doi pointeri sunt egali dac adresele care constituie valorile lor sunt egale. Privind memoria intern liniar, ncepnd de la 0x0000:0x0000, un pointer p este mai mare dect altul q, dac adresa pe care o conine p este mai ndeprtat de nceputul memoriei dect adresa coninut de q. Este permis i compararea unui pointer cu o valoare constant. Uzual se folosete comparaia cu valoarea NULL pentru a verifica dac pointerul a fost iniializat (un pointer neiniializat are valoarea NULL), folosind unul din operatorii == sau !=. Valoarea NULL este definit n stdio.h astfel: #define NULL 0
Programarea calculatoarelor
De multe ori se prefer comparaia direct cu zero (nume==0 sau nume!=0). n loc de nume=0 se poate folosi expresia nume. Aceasta se interpreteaz astfel: dac nume nu a fost iniializat, atunci are valoarea NULL (adic 0), deci expresia este fals. n caz contrar valoarea expresiei este nenul, deci adevrat. Asemntor se folosete expresia !nume. Exemplu:
float* p,q,r,t; float a,b; p=&a; q=&b; r=&a; a=5; b=7; if(t) printf("Pointer initializat!\n"); else printf("Pointer neinitializat!\n"); if(p==r) printf("Pointeri egali\n"); else printf("Pointeri diferiti\n"); if(p>q) printf("%d\n",a); else printf("%d\n",b);
Pe ecran se va afia:
Pointer neinitializat! Pointeri egali 7
deoarece t are valoarea NULL, variabilele p i r au ca valoare adresa lui a, iar q conine adresa lui b, care este mai mare dect a lui a (datorit faptului c a a fost alocat primul). Diferena dintre doi pointeri Fie secvena:
int m[50],* a, * b; a=&m[i]; b=&m[j];
unde i i j sunt ntregi n intervalul [0..49]. Expresia a-b are valoarea i-j, interpretat ca distan ntre adresele a i b, exprimat n zone de memorie de lungime sizeof(int). Valoarea unei expresii diferen se calculeaz astfel: se face diferena ntre cele dou adrese (n octei), apoi se mparte la dimensiunea tipului de dat referit de cei doi pointeri (tipul int n exemplul de mai sus vezi figura 1.2). Cei doi pointeri trebuie s refere acelai tip de dat, altfel rezultatul nu are semnificaie. Operaia este util n lucrul cu masive.
m i j
m[4]
m[49]
m[49,0] m[49,1]
Figura 1.3 Reprezentarea modului de alocare dinamic a spaiului necesar pentru memorarea unei matrice 50x50
Programarea calculatoarelor
Exemple: un masiv cu trei dimensiuni float m[10][10][10] poate fi interpretat ca un pointer spre un vector de pointeri spre matrice; un masiv cu n dimensiuni este tratat ca un pointer spre un vector de pointeri ctre masive cu n-1 dimensiuni. Pentru a lucra cu elementele unei matrice se poate folosi adresarea indexat (m[i] pentru vectori sau m[i][j] pentru matrice) sau adresarea elementelor prin pointeri (*(m+i) pentru vectori sau *(*(m+i)+j) pentru matrice etc.). De asemenea se poate declara un pointer iniializat cu adresa de nceput a masivului, iar elementele masivului s fie referite prin intermediul acestui pointer. Exemple:
float* v[10]; p=v; float* p;
Dup atribuire, pointerul p conine adresa de nceput a masivului i poate fi folosit pentru referirea elementelor masivului. De exemplu, v[3] i p[3] refer aceeai zon de memorie. S se scrie secvena de program care citete de la tastatur elementele unei matrice, folosind un pointer pentru adresarea elementelor matricei.
int m,n; float a[10][10]; printf("Nr. linii:\n"; scanf("%d", &m); printf("Nr. coloane:\n"); scanf("%d", &n); for(i=0;i<m;i++) for(j=0;j<n;j++) { printf("a(%d,%d)= ",i,j); scanf("%f", *(m+i)+j ); }
Observaie: *(m+i)+j este un pointer, care conine adresa elementului a[i][j]; n funcia scanf trebuie transmise ca parametri adresele unde se depun valorile citite; n exemplul anterior se putea scrie &*(*(m+i)+j), i, reducnd, rezult *(m+i)+j.
Funcia rezerv o zon de n octei n heap i returneaz adresa acesteia. Deoarece funcia returneaz pointer spre void este necesar conversia spre tipul dorit, astfel:
int* nume; nume=(int *) malloc(sizeof(int));
Eliberarea unei zone de memorie rezervate anterior se face prin funcia standard:
void free(void* p);
Funcia primete ca parametru un pointer (indiferent de tip) spre zona de memorie pe care trebuie s o elibereze. Limbajul C ofer posibilitatea de a aloca contiguu zone de memorie pentru mai multe date de acelai tip, prin funcia standard:
void* calloc(unsigned nr_elem, unsigned dim_elem);
Funcia calloc rezerv o zon contigu de memorie pentru mai multe elemente de acelai tip, ntorcnd un pointer spre zona respectiv.
int* masiv; masiv=(int*)calloc(50,sizeof(int));
rezerv spaiu de
memorie pentru un vector cu 50 de elemente ntregi. Exist i o variant a lui malloc care returneaz n mod explicit un pointer ndeprtat (far):
void* farmalloc(unsigned long n);
Pentru eliberarea unei zone de memorie rezervate prin farmalloc se folosete funcia standard:
void farfree(void* p);
2. S se scrie un subprogram pentru citirea de la tastatur a dimensiunii i elementelor unui vector memorat n heap.
void cit_vect(int *n, float **v) { int i; printf("Nr. elemente: "); scanf("%d ", n); *v=(float*)malloc(*n*sizeof(float));
Programarea calculatoarelor
for(i=0;i<*n;i++) { printf("v(%d)= ",i); scanf("%f",&(*v)[i]); } }
3. S se scrie o funcie care s citeasc cel mult n numere ntregi i le pstreze n zona de memorie a crei adres de nceput este dat printr-un pointer. Funcia returneaz numrul valorilor citite.
int cit_nr(int n, int* p) { int nr, i; int* q=p+n; // q este adresa unde se termina zona //rezervata pentru cele n numere i=0; while(p<q) //cit timp nu s-au citit n numere { printf("Numarul %d= ", i); if(scanf("%d", &nr)!=1) break; //in caz de eroare la citire //se termina ciclul *p=nr; p++; i++; } return(i); }
sau
Declaraia este echivalent cu tip nume=valoare dar, n plus, nu permite modificarea valorii lui nume printr-o expresie de atribuire nume = valoare_noua; Fa de o constant simbolic, n acest caz se rezerv spaiu de memorie n care se nscrie valoarea constantei (constant obiect). b)
tip const* nume = valoare; const tip* nume = valoare;
sau
Prin aceast declarare se definete un pointer spre o zon cu valoare constant. Nu este permis atribuirea de genul *nume=valoare_noua, dar se poate ca variabilei nume s i se atribuie o adres (de exemplu, nume = p, unde p este un pointer spre tip). Pentru a modifica valoarea nscris n memorie la adresa memorat de
c)
Construcia se folosete la declararea parametrilor formali, pentru a mpiedica modificarea lor n corpul subprogramelor, n cazul n care apelatorul are nevoie de valorile iniiale.
unde argc conine numrul de parametri ai programului, incrementat cu 1. Exemplu: Dac programul nu are niciun parametru, argc are valoarea 1, dac programul are doi parametri, argc are valoarea 3 etc. Variabila argv este un vector de pointeri care conine adresele de memorie unde s-au stocat irurile de caractere care constituie parametrii programului. Primul ir (argv[0]) conine identificatorul fiierului (inclusiv calea complet) care memoreaz programul executabil. Urmtoarele iruri conin parametrii n ordinea n care au aprut n linia de comand (parametrii n linia de comand sunt iruri de caractere separate prin spaii). Interpretarea acestor parametri cade n sarcina programului. Exemplu: S se scrie un program care afieaz parametrii din linia de comand.
#include<stdio.h> main(int argc, char *argv[]); { int i; printf("Fisierul executabil: %s\n", argv[0]); for(i=1;i<argc;i++) printf("Parametrul nr. %d: %s\n",i, argv[i]); }