Tematică științifică · 4.2
Operațiile de alocare și eliberare
Tipul pointer și tipul referință în C++, operatorii pentru pointeri, definirea tipurilor de date, alocarea cu new pentru date simple, înregistrări și tablouri, accesarea datelor alocate dinamic și eliberarea memoriei cu delete.
În C++, datele alocate dinamic se prelucrează cu ajutorul pointerilor. Un program care folosește alocarea dinamică definește tipurile de date necesare, alocă memorie pentru date, le accesează prin adresele lor și eliberează memoria în momentul în care datele nu mai sunt necesare.
Tipul pointer
Un pointer este o variabilă care memorează adresa unei zone de memorie. Declararea unui pointer are forma generală:
tip *nume_pointer;
tip se numește tipul de bază al pointerului și este tipul datei aflate la adresa memorată.
Adresa arată numai primul octet al zonei. Din tipul de bază, compilatorul află câți octeți ocupă
data și cum trebuie interpretat conținutul lor: în 4 octeți se poate păstra la fel de bine un
int sau un float, iar aceeași secvență de biți are valori diferite în cele două cazuri.
Pointerul p poate păstra numai adresa unei date de tip int, r numai adresa unei date de tip
double, iar a numai adresa unei înregistrări de tip punct.
int *p;
double *r;
struct punct {
int x, y;
};
punct *a;
Constanta NULL. Valoarea NULL, egală cu 0, arată că pointerul nu memorează adresa niciunei
date. Ea se folosește la inițializarea pointerilor și la marcarea sfârșitului unei structuri de
date înlănțuite, iar testul p == NULL arată dacă pointerul indică o dată.
Pointerul neinițializat. Un pointer local căruia nu i s-a atribuit nicio valoare conține o valoare reziduală, adică o adresă oarecare. Scrierea la această adresă produce de obicei o eroare la execuție sau modifică, fără niciun semnal, alte date ale programului.
Operatorii pentru pointeri
Referențierea și dereferențierea
Operatorul & furnizează adresa unei variabile, iar operatorul * furnizează data aflată la
adresa memorată într-un pointer.
| Operatorul | Operația | Se aplică pe | Rezultatul |
|---|---|---|---|
& | referențierea | o variabilă sau un element de tablou | adresa acestora, de tip pointer |
* | dereferențierea | un pointer | data de la adresa memorată, de tipul de bază al pointerului |
Expresia *p se poate folosi oriunde se poate folosi o variabilă de tipul de bază: într-o
expresie, în membrul stâng al unei atribuiri sau la citire, în cin >> *p.
Instrucțiunile a = 15; și *p = 15; au același efect cât timp p memorează adresa lui a.
Prima folosește adresarea directă, prin nume, iar a doua adresarea indirectă, prin
adresă.
int a = 10, b = 20;
int *p = &a; // p memoreaza adresa lui a
cout << *p; // 10, valoarea lui a
*p = 15; // se modifica a
cout << a; // 15
b = *p + 1; // b primeste 16
p = &b; // acum p indica variabila b
cout << *p; // 16
Accesul la câmpurile unei înregistrări
Dacă a este un pointer către o înregistrare, *a este înregistrarea, iar un câmp al ei se
scrie (*a).x. Parantezele sunt necesare, fiindcă operatorul . are prioritate mai mare decât
operatorul *: fără ele, *a.x s-ar citi *(a.x) și ar produce o eroare de compilare.
Aceeași operație se scrie mai simplu cu operatorul ->, numit operatorul de selecție
indirectă: a->x este echivalent cu (*a).x.
Prin a->x și a->y se ajunge la câmpurile înregistrării m, a cărei adresă este memorată în
a.
punct m = {3, 4};
punct *a = &m;
cout << (*a).x << " " << (*a).y; // 3 4
cout << a->x << " " << a->y; // 3 4
Atribuirea
Unui pointer i se poate atribui:
- adresa unei variabile de tipul de bază,
p = &a;; - valoarea altui pointer cu același tip de bază,
q = p;, după care cei doi pointeri indică aceeași zonă de memorie; - numele unui tablou cu elemente de tipul de bază,
p = v;, echivalent cup = &v[0];; - constanta
NULL; - adresa furnizată de operatorul
new.
Numele unui tablou este o constantă de tip adresă: el are ca valoare adresa primului element și
nu poate fi modificat. De aceea atribuirile v = p; și v = w;, cu v și w tablouri, produc
erori de compilare.
Operațiile aritmetice
Asupra pointerilor se pot efectua adunarea și scăderea unui număr întreg, incrementarea,
decrementarea și scăderea a doi pointeri cu același tip de bază. Unitatea în care se calculează
este sizeof(tip), adică lungimea unei date de tipul de bază.
| Expresia | Rezultatul |
|---|---|
p + k | adresa aflată cu k elemente după p, adică adresa din p plus k · sizeof(tip) |
p - k | adresa aflată cu k elemente înaintea lui p |
p++, p-- | pointerul trece la elementul următor, respectiv la cel precedent |
q - p | numărul de elemente dintre cele două adrese |
Operațiile aritmetice au sens numai dacă pointerii indică elemente ale aceluiași tablou. În afara unui tablou, la adresa obținută nu se află o dată a programului.
Legătura dintre pointeri și tablouri decurge din aceste reguli. Dacă v este un tablou, v + i
este adresa elementului v[i], iar *(v + i) este chiar elementul v[i]. Un tablou se poate
parcurge cu un pointer q, care pornește de la v și se oprește la v + n.
int v[5] = {10, 20, 30, 40, 50};
cout << *(v + 2); // 30, adica v[2]
for (int *q = v; q < v + 5; q++)
cout << *q << " "; // 10 20 30 40 50
Operatorii relaționali
Operatorii == și != arată dacă doi pointeri memorează aceeași adresă și se folosesc mai ales
în comparația cu NULL. Operatorii <, <=, > și >= au sens pentru pointeri care indică
elemente ale aceluiași tablou: p < q înseamnă că elementul indicat de p se află înaintea celui
indicat de q.
Tipul referință
O referință este un al doilea nume pentru o variabilă existentă. Referința și variabila desemnează aceeași zonă de memorie, deci orice modificare făcută prin unul dintre nume se vede și prin celălalt.
tip &nume_referinta = variabila;
Referința se supune regulilor următoare:
- se inițializează obligatoriu la declarare;
- după inițializare, nu mai poate fi legată de altă variabilă:
b = c;copiază valoarea luicîna; - are același tip ca variabila la care se referă.
int a = 10, c = 3;
int &b = a; // b este un alt nume pentru a
b = 25;
cout << a; // 25
b = c; // a primeste valoarea 3
cout << a; // 3
Tipul referință se folosește la transmiterea parametrilor prin referință, prezentată în
subcapitolul Subprograme definite de utilizator.
La alocarea dinamică se transmite frecvent prin referință chiar un pointer, cu parametrul scris
tip *&p. Subprogramul poate modifica atunci adresa memorată în pointerul din modulul apelant,
ceea ce se întâmplă la orice alocare făcută într-un subprogram.
Parametrul p este o referință la pointerul q din main. Adresa rezervată de new ajunge astfel
în q.
Fără &, subprogramul ar lucra cu o copie a lui q. La revenire, q ar avea tot valoarea NULL,
iar adresa zonei alocate s-ar pierde odată cu copia.
void aloca(int *&p, int x) {
p = new int;
*p = x;
}
int main() {
int *q = NULL;
aloca(q, 7);
cout << *q; // 7
delete q;
return 0;
}
Materialul acesta se citește pe educamp.ro și nu se tipărește.
S-a încheiat minutul de citit liber.
Ce cuprinde subcapitolul
- Tipul pointer
- Operatorii pentru pointeri
- Tipul referință
- Definirea tipurilor de date necesare
- Alocarea memoriei
- Accesarea datelor alocate dinamic
- Eliberarea memoriei
- Greșeli frecvente
- Apariții la examen
Continuă lectura ca și cursant
Cel puțin un subcapitol din fiecare capitol este disponibil gratuit și integral. Pentru a citi toate celelalte subcapitole ale disciplinei, te înscrii la cursul de pregătire.
Prima săptămână este gratuită, fără plată și fără card. Dacă vrei să vezi mai întâi cum este prezentată materia, poți reveni la primul subcapitol al capitolului.
100 RON / lună, pentru o disciplină
Ce cuprinde:
- Două întâlniri de câte două ore, în fiecare lună
- Tot suportul de curs publicat până acum la disciplina aleasă
- Capitole noi în fiecare săptămână, cuprinse în luna plătită, fără costuri suplimentare
- Material organizat după structura programei de examen
- Acces de pe orice dispozitiv, folosind același cont
- Prima săptămână gratuită, fără card și fără reînnoire automată
Începe săptămâna gratuită Sunt cursant — login
Află când publicăm materiale noi
Materia este publicată treptat, capitol cu capitol. Înscrie-te pentru a primi un e-mail atunci când apare un capitol nou de informatică.
Nu am putut înregistra adresa. Verifică e-mailul și materia aleasă, apoi încearcă din nou.
Surse
- Cerchez, E., Șerban, M., „Programarea în limbajul C/C++ pentru liceu”, Editura Polirom, Iași, 2005
- „Informatică. Manual pentru clasa a XI-a”, Editura Didactică și Pedagogică, București
- Programa pentru examenul de definitivare în învățământ, disciplina Informatică, cap. 4 „Alocarea dinamică a memoriei”
- Programa pentru concursul de ocupare a posturilor didactice, disciplina Informatică, cap. 4 „Alocarea dinamică a memoriei”
- Subiecte și bareme publicate, Titularizare, informatică, 2009, 2017, 2018, 2021