Alocarea dinamica
Memoria interna este împartita în octeti (bytes), fiecare continând 8 biti de informatie. Fiecare octet are o adresa unica.
Un program executabil consta din cod (instructiuni masina corespunzatoare instructiunilor programului C++) si date (variabilele programului C++). Fiecare variabila din program ocupa unul sau mai multi octeti din memorie. Adresa primului octet este adresa variabilei.
Definitie. Un pointer este o adresa. O variabila pointer 434l116e este o variabila capabila sa stocheze adrese.
Declarare
O variabila pointer se declara ca o variabila obisnuita, singura diferenta constând în faptul ca variabila pointer este precedata de asterisc.
Exemplu: int *p;
Declaratia de mai sus ne spune ca variabila pointer p este capabila sa pointeze catre obiecte de tip int.
Operatori de adresare si indirectare
Limbajul C++ pune la dispozitia programatorilor 2 operatori pentru lucrul cu pointeri:
a) operatorul de adresare &. Daca x este o variabila, atunci &x este adresa lui x în memorie.
b) operatorul de indirectare *. Se foloseste pentru a accesa obiectul catre care pointeaza un pointer.
Fie declaratia:
int i, *p;
Este crucial sa initializam variabila p înainte de a o utiliza. O metoda este sa-i atribuim adresa unei alte variabile, de exemplu:
p=&i;
Observatie. &i nu este un obiect de tip lvalue (ceea ce înseamna ca nu poate aparea în stânga unei atribuiri), altfel spus, adresa unei variabile statice este constanta.
Observatie. De asemenea, p fiind o variabila pointer, constructia &p este corecta si nu poate fi lvalue.
Fie urmatoarele linii de cod:
int i, *p;
p=&i;
Consecinta acestora se poate urmari în figura de mai jos
Memoria interna |
|
|
|
p=&i | |
|
|
&p |
Fig. 1
Sa analizam urmatoarele linii de cod
int i=2, *p;
p=&i;
cout<<i; // se afiseaza 2
cout<<*p; // se afiseaza 2
*p=5;
cout<<i; // se afiseaza 5
cout<<*p; // se afiseaza 5
Daca p pointeaza catre valoarea i, atunci *p este un alias pentru i. Dupa cum se vede în fig. 1, *p si i au aceeasi valoare si mai mult, schimbarea valorii *p schimba si valoarea i.
Atentie!!! Nu se aplica operatorul de indirectare unei variabile pointer neinitializate, asa cum s-a procedat în exemplul de mai jos
int *p;
*p=1;
Pot aparea erori deoarece p poate pointa catre orice adresa de memorie, ca urmare se modifica o locatie necunoscuta. Locatia modificata poate fi a programului (putând cauza functionarea eronata a acestuia) sau a sistemului de operare (putând cauza o cadere a sistemului).
Atribuirea pointerilor
Limbajul C++ accepta folosirea operatorului de atribuire pentru a copia pointeri. Liniile de cod de mai jos ilustreaza acest aspect. Atentie însa a nu se confunda p=q cu *p=*q.
int i=1, j=3, *p, *q;
p=&i;
q=p; // în acest moment p si q pointeaza catre valoarea i
*p=2;
cout<<i; // se afiseza 2
cout<<*p; // se afiseza 2
cout<<*q; // se afiseza 2
q=&j;
cout<<*q; // se afiseza 3
*q=*p; // se modifica doar valoarea de la adresa q
*p=1;
cout<<i; // se afiseza 1
cout<<*p; // se afiseza 1
cout<<*q; // se afiseza 2
Alocarea memoriei
O a doua metoda de initializare a unei variabile pointer consta în alocarea dinamica a memoriei folosind operatorul new, a carui sintaxa este prezentata mai jos
tip *p;
p = new tip [n];
Efectul instructiunii de mai sus este încercarea de a aloca o zona de memorie continua suficienta pentru a memora n valori de tipul tip. Variabila pointer p va fi initializata cu adresa primului octet al zonei de memorie, în cazul în care operatia reuseste sau cu NULL în caz contrar. Se impune deci, ca înainte de a folosi variabila p sa testam rezultatul încercarii de a aloca memorie.
Observatie. Clauza [n] este optionala. În cazul în care lipseste se considera n
Eliberarea memoriei
Eliberarea memoriei alocata dinamic se face fie utilizând operatorul delete, fie la terminarea programului.
Pointerii si vectorii. Aritmetica pointerilor
O variabila pointer poate pointa catre un element al unui vector. Acest fapt este interesant deoarece folosind aritmetica pointerilor putem accesa si celelalte elemente ale vectorului.
Pentru exemplele de mai jos vom considera declaratia
int a[10], *p, *q, i, j;
Limbajul C++ suporta urmatoarele forme ale aritmeticii pointerilor
a) Adunarea unui întreg la o variabila pointer
Adunând o valoare j la o variabila pointer p, rezulta un pointer la elementul aflat la j pozitii dupa elementul catre care pointeaza p. Mai exact, daca p pointeaza catre a[i], atunci p+j va pointa catre a[i+j] (daca a[i+j] exista).
Observatie. Trebuie sa ne asiguram ca pointerul rezultat reprezinta o adresa valida.
Observatie. La nivel de octet se adauga j*sizeof(*p) octeti pentru a asigura mutarea peste j elemente.
b) Scaderea unui întreg dintr-o variabila pointer
Similar ca la adunare cu observatia ca mutarea se face în sens invers. Deci, daca p pointeaza catre a[i], p-j va pointa catre a[i-j]. si în acest caz trebuie sa ne asiguram ca adresa rezultata este valida.
c) Diferenta a 2 pointeri
Rezultatul întors este distanta în elemente (nu în octeti) între cele 2 adrese. De exemplu, daca p pointeaza catre a[i] si q catre a[j], atunci p-q=i-j si q-p=j-i.
Compararea a 2 pointeri
Se pot folosi operatorii relationali <, <=, >=, >, ==, !=. Rezultatul compararii depinde de pozitia relativa în vector. Ca si diferenta a 2 pointeri, compararea a 2 pointeri are sens când ambii pointeaza catre elemente din acelasi vector.
Pentru a testa cele enuntate mai sus, vom scrie câte un program pentru a determina suma celor n elemente ale unui vector atât în varianta clasica cu vectori cât si în varianta cu pointeri
int a[10], n, i, s=0; cin>>n; for(i=0; i<n; i++) cin>>a[i]; for(i=0; i<n; i++) s+=a[i]; cout<<s; |
int a[10], n, *p, s=0; cin>>n; for(p=a; p<a+n; p++) cin>>*p; for(p=a; p<a+n; ) s+=*p++; cout<<s; |
În tabelul de mai jos, vom evidentia semnificatia diferitelor variante în care apar operatorii si ++ (semnificatii valabile si pentru cazul --, operatorul * are prioritate mai mare decât si
Expresia |
Semnificatia |
*p++ sau *(p++) |
Valoarea expresiei este *p înainte de incrementare, dupa care se incrementeaza p |
(*p)++ |
Valoarea expresiei este *p înainte de incrementare, dupa care se incrementeaza *p |
*++p sau *(++p) |
Incrementeaza p, valoarea expresiei este *p dupa incrementare |
++*p sau ++(*p) |
Incrementeaza p, valoarea expresiei este *p dupa incrementare |
Folosirea numelui vectorilor ca pointer
Mai exista o legatura între vectori si pointeri si anume numele unui vector poate fi folosit ca un pointer catre primul element din vector
Exemplu
int a
*a=1; // memoreaza valoarea 1 în a
*(a+1)=2; // memoreaza valoarea 2 în a
În general, a+i este echivalent cu &a[i] si *(a+i) este echivalent cu a[i].
Având în vedere aceste echivalente si pastrând vie în memorie proprietatea adunarii de comutativitate, rezulta o consecinta interesanta si anume a[i] este echivalent cu i[a]
Demonstratie
a[i]=*(a+i)=*(i+a)=i[a]
|