Curs C++ | Sectiunea 15 – Pointeri
Pointerii sunt de asemenea si valori, dar diferite de cele folosite pana acum. Tipurile pe care le-am folosit sunt strans legate de procesarea datelor si reflecta clar rolul lor, precum un int, un char etc.
🖥️ Calculatoarele moderne au o memorie foarte mare si rapida. Stim deja ca memoria este masurata in unitati numite bytes (octeti) si mai stim ca in momentul declararii unei variabile, aceasta ocupa o mica portiune din memoria calculatorului. Ne-a interesat pana in acest moment doar ce valoare stocam, de acum incolo suntem interesati si de locul in care stocam.
💡 Zona in care este stocata valoarea este foarte des intalnita sub numele de adresa. Asa cum si noi locuim la o anumita adresa, asa si valoarea noastra “locuieste” la o anumita adresa 🙂 .
❗ Astfel, deducem urmatoarea diferenta:
- valoarea variabilei reprezinta ce se stocheaza
- adresa variabilei este informatia despre unde este stocata
💡 Deci putem spune ca pointerii sunt folositi pentru a stoca adresa oricarui tip de date.
Primul pointer
❗ Sa analizam exemplul de mai sus. Mai intai declaram o variabila numita p. Ne-am grabi sa spunem ca este un int, dar nu este . Operatorul pe care pana acum l-am folosit doar ca inmultire, “*“, ne spune acum faptul ca p este un pointer si va stoca adresa unei variabile de tip int.
Mereu pointerii sunt folositi pentru a indica la un tip de date mentionat in declarare. C++ ne ofera si asa zisii amorphous pointers, ce pot fi folositi pentru a indica la orice tip de date. Vom discuta despre ei mai tarziu insa.
❓ Ce tip este variabila p? Exista mai multe raspunsuri, reprezentand acelasi lucru dar diferite in exprimare. Putem spune ca p este o variabila de tip pointer la int. Mai putem sa spunem si ca p este o variabila de tip int *.
NULL Pointer
❓ Asa cum ne-am astepta, putem asigna o valoare unui pointer la fel cum am procedat pentru orice alt tip, folosind operatorul de asignare “=“. Intrebarea principala este, ce valori putem sa atribuim?
❗ Folosirea unui literal nu este o varianta. Compilatorul nu ne va lasa sa scriem precum in exemplul de mai jos. In primul rand pentru ca sintaxa de C++ nu ne permite iar in al doilea rand, find mai degraba un motiv logic, pentru ca nu am putea stii dinainte ce se afla la adresa 12345.
In urma rularii, compilatorul ne va returna urmatoarea eroare: invalid conversion from ‘int’ to ‘int*’. O eroare destul de directa si clara, nu putem asigna o valoare de tip int unei variabile de tip int *.
Exista totusi o valoarea pe care o putem atribui, si aceasta este zero. Compilatorul nu va avea nicio problema in acest caz.
💡 Un pointer caruia ii asignam valoarea zero se numeste null pointer (latina -> nullus – none). Acesa nu indica la nimic iar in ciuda aparentelor, poate deveni foarte util in anumite situatii.
❗ Din cauza faptului ca atribuirea valorii zero unui pointer poate cauza uneori neintelegeri si greseli, exista o regula nescrisa de a evita o astfel de scriere. In loc, vom scrie in felul urmator:
📝 Simbolul NULL este in realiate egal cu zero. Arata ca o variabila dar nu ii putem modifica valoarea. Poarta si numele de macro. Nu va faceti probleme, vom discuta in viitor despre ele 😉 .
❗ Aceeasi conventie ne mai spune si ca NULL trebuie asignat doar pointer-ilor. Astfel, vom avea un cod elegant si care se va plia cu stilul general (coding style).
Asignarea unei valori reale
💡 Totusi, cum asignam o valoare semnificativa unui pointer, diferita de NULL? Putem atribui o valoare ce indica la o variabila deja existenta. Vom avea nevoie de operatorul “&“, numit si operator referinta (este in acelasi timp si un operator cu prefix unar).
Operatorul referinta gaseste adresa argumentului. Se foloseste ca in exemplul urmator:
In urma asignarii, variabila p va indica la zona de memorie une variabila x este stocata. Cum il putem insa folosi odata atribuita valoarea? Il putem dereferentia (dereference).
📝 Dereferentierea este o operatie prin care variabila pointer devine sinonima cu valoarea la care indica, altfel spus, putem accesa valoarea salvata la adresa respectiva. Acest lucru se realizeaza tot prin operatorul “*“. Prin plasarea in fata pointeru-ului nostru, obtinem valoarea stocata la adresa de memorie, il dereferentiem.
❗ Atentie, in momentul declararii, operatorul steluta “*” are rolul de a defini tipul, ulterior cand este folosita variabila, are rolul de dereferentiere.
Sa discutam si despre un exemplu simplu si cursiv:
In exemplul anterior am pus in practica noul operator mentionat iar lucrurile ar trebui sa fie destul de clare, aproape clare 🙂 . Sa analizam putin linia 8.
Nu modificam valoarea pointer-ului, adica adresa la care indica, modificam valoarea care este stocata la acea adresa. De aceea, in momentul afisarii, valoarea lui x s-a modificat de asemenea, pentru ca atat x cat si p indica la aceeasi adresa.
Sa recapitulam la un mod mai general. Daca declaram un pointer de forma:
- variabila pointer este de tipul ANY_TYPE*
- expresia *pointer este de tipul ANY_TYPE
❗ Nu uitati, a dereferentia un pointer NULL este interzis si poate cauza probleme foarte mari foarte rapid!
Operatorul sizeof
💡 Acesta este un operator unar cu prefix ce are cea mai inalta prioritate posibila. Exista totusi o diferenta fundamentala fata de operatorii studiati pana acum. Nu necesita o variabila ca argument, precum ne-am obisnuit. Asteapta un literal, o variabila, o expresie sau numele tipului de date.
❗ Numele lui este destul de sugestiv, ne ofera informatii despre cati octeti(bytes) ocupa in memorie argumentul furnizat. Mai mult, nu este doar operator ci si keyword.
Cel mai bun prim exemplu ar fi sa vedem cat ocupa fiecare tip de date pe care l-am studiat pana acum. In acest mod intelegem si cum calculatorul si compilatorul folosesc memoria.
Aici se termina si aceasta sectiune 🙂 . Pentru intrebari suplimentare puteti folosi informatiile de aici.
➡ Sectiunea anterioara:Curs C++ | Sectiunea 14 – Structuri
➡ Sectiunea urmatoare:Curs C++ | Sectiunea 16 – Pointeri Aritmetici

