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 |
|
i |
|
p=&i | |
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]
|