Sunteți pe pagina 1din 10

1 Tipuri dinamice de date.

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).

Tipuri dinamice de date. Pointeri

1.1 Declararea i iniializarea pointerilor


Fie TIP un tip de dat oarecare n limbajul C (inclusiv void). Declararea TIP* nume; este o declaraie de pointer. TIP* este un nou tip de dat denumit pointer spre TIP, iar nume este o variabil de tipul pointer spre TIP. Exemple: n este o variabil de tip pointer spre ntreg; int* n; struct complex {a,b:real;}* x; x este o variabil de tip pointer spre o structur de tipul complex; void* p; p este o variabil de tip pointer spre void; p poate primi ca valoare adresa unei zone de memorie de orice tip. Dac TIP este un tip oarecare (mai puin void) atunci tipul TIP* este adresa unei zone de memorie de un tip cunoscut. Operaiile care se pot efectua asupra zonei respective de memorie sunt definite de tipul acesteia. Dac TIP este void, atunci TIP* este adresa unei zone de memorie de tip necunoscut. Deoarece nu se cunoate tipul zonei de memorie, nu sunt definite operaiile care se pot efectua asupra ei. Pentru pointerii din exemplele anterioare se rezerv n memoria principal (n segmentul de date) cte o zon de 4B n care se va memora o adres (sub forma segment:offset). Cnd variabila nume nu este iniializat prin declarare, ea primete implicit valoarea NULL. La execuie, poate primi ca valoare adresa unei variabile numai de tipul TIP. Dac TIP este void, atunci nume poate primi adresa oricrei variabile, de orice tip. Exemple:
int* nume; int a; float b; nume = &a; => este o atribuire corect; nume are ca valoare adresa variabilei a. nume = &b; => este o atribuire incorect; nume poate primi ca valoare doar

adresa unei variabile ntregi.


void* nume; int a; float b; nume = &a; nume = &b; =>

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.

1.2 Utilizarea pointerilor


1.2.1 Operaii cu pointeri
Asupra pointerilor se pot efectua operaii aritmetice. Fie secvena:
int *nume,*nume2, c, a, b; nume=&a; nume2=&a;

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);

Situaia iniial este urmtoarea:

nume 4B

nume2 4B

a 2B 2B

b 2B

Tipuri dinamice de date. Pointeri

Dup cele dou operaii:


nume 4B nume2 4B c a 2B 2B b 2B

Analog se execut operaiile ++nume i --nume. Exemplu:


float v[20]; float* p; int i; p=&v[i];

=> i poate avea valori ntre 0 i 19

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

Figura 1.2 Reprezentarea semnificaiei variabilelor din exemplul anterior

Tipuri dinamice de date. Pointeri

1.2.2 Legtura ntre pointeri i masive


n limbajul C numele unui masiv este un pointer ctre tipul de dat al elementele masivului. Pentru masivele unidimensionale: int m[50]; m are tipul int* int* p; p are tipul int* Diferena const n faptul c zona de memorie ctre care puncteaz m este rezervat la compilare (ceea ce nu se ntmpl n cazul pointerilor declarai ca atare). De aceea m nici nu poate primi valori n timpul execuiei programului (nu se poate schimba adresa memorat n m). El memoreaz adresa primului element din masiv. Referirea unui element m[i] este echivalent cu *(m+i) coninutul de la adresa m+i. Limbajul C nu face niciun fel de verificri n privina depirii limitelor indicilor masivului, de aceea expresiile m[500] sau m[-7] vor fi considerate corecte de compilator, existnd riscul unor erori logice. Este sarcina programatorului s se asigure c indicii nu vor depi limitele. Pentru masivele bidimensionale: m are semnificaia urmtoare: m[i][j] *(*(m+i)+j), reprezint coninutul de la adresa j plus coninutul de la adresa memorat n i plus m. Aceasta poate fi interpretat astfel: m este un pointer spre un vector de pointeri, fiecare element al vectorului fiind la rndul lui un pointer spre o linie a matricei (un vector de elemente de tip float). n acest fel se aloc matricele n mod dinamic (figura 1.3). Analog pot fi interpretate masivele cu mai multe dimensiuni.
int m[50][50];

m m[0] m[1] m[2] m[3]

m[0,0] m[0,0] m[2,0] m[3,0] m[4,0]

m[0,1] m[0,1] m[2,1] m[3,1] m[4,1]

m[0,49] m[0,49] m[2,49] m[3,49] m[4,49] m[49,49

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.

1.2.3 Alocarea dinamic a memoriei


Pentru a memora o valoare de un anumit tip n heap este necesar s se declare un pointer ctre acel tip de dat, apoi s se rezerve memoria necesar. Pentru a rezerva spaiu n heap se folosete funcia standard:
void* malloc(unsigned n);

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));

rezerv n heap spaiu

pentru o valoare de tip ntreg.

Tipuri dinamice de date. Pointeri

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);

Exemple: 1. Alocarea de spaiu n heap pentru o matrice.


int** m; int n,p; /* se aloc spaiu pentru vectorul cu adresele celor n linii ale matricei */ m=(int**)malloc(m*sizeof(int*)); for(int i=0;i<m;i++) /*se aloc spaiu pentru fiecare linie a matricei, cte p elemente*/ m[i]=(int*)malloc(n*sizeof(int));

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); }

1.2.4 Modificatorul const


n limbajul C constantele simbolice se declar prin directiva de preprocesare #define. O alt posibilitate de lucru cu constante este iniializarea unei variabile cu o valoare i interzicerea modificrii valorii acesteia. n acest scop se folosete modificatorul const. Sunt permise urmtoarele forme de utilizare:
a) tip const nume = valoare; const tip nume = valoare;

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

Tipuri dinamice de date. Pointeri

pointerul nume se poate folosi totui un alt pointer:


tip *t; t=nume; *t=valoare_noua;

c)

const tip* nume;

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.

1.2.5 Tratarea parametrilor din linia de comand


n linia de comand a unui program pot s apar parametri (sau argumente). Acetia sunt iruri de caractere desprite prin spaii. Programul poate accesa argumentele prin intermediul parametrilor predefinii ai funciei main:
void main(int argc, char* argv[])

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]); }

S-ar putea să vă placă și