start     Articole     Despre mine    

Ce sunt pointerii si la ce sunt ei buni in programare

Multi programatori incepatori sufera de pointerofobie — adica o teama inexplicabila de pointeri. Si cum nu se poate scapa de teama de “Bau-bau”-ul din dulap decat mergand la dulap, deschizandu-l si vazand ca nu e acolo nimic de speriat, in acest articol te voi lua de mana si te voi duce la “dulapul” cu pointeri.

OK, dar mai intai: Ce sunt pointerii (caci poate nici macar n-ai auzit vreodata de ei)? Si la ce sunt ei buni?

Incep cu intrebarea “La ce sunt buni?” — ca sa intelegi ca sunt foarte importanti si ca merita sa studiezi cu mare atentie explicatiile ce urmeaza.

La ce sunt buni pointerii?

Folosesti Google si Facebook? Ai vazut cat de repede cauta informatiile solicitate (site-uri, conturi) printre milioanele de informatii disponibile?

Cum crezi ca se face asta? Oare la fiecare cautare ceruta de tine Google isi parcurge toate site-urile pe care le are inregistrate si le compara unul cate unul cu textul cautat?

N-ar fi prea placut sa se intample asa. Ti-ar trebui rabdare. Multa rabdare. Chiar extrem de multa rabdare.

Secretul cautarilor Google (ca si al oricarei aplicatii serioase pe care o folosesti (inclusiv browserul de pe care citesti randurile astea)) sunt structurile de date. Adica modalitati de organizare isteata a datelor.

Adica (mai pe romaneste (intr-o romana specifica celor care deja stiu bazele programarii)) e vorba despre modalitati de grupare isteata a variabilelor.

Doua modalitati de grupare a variabilelor ai vazut deja in lectia despre vectori si matrici. Da si vectorii si matricile sunt structuri de date si ne permit sa facem multe lucruri. Dar modalitatea prin care ele grupeaza variabilele e destul de limitata — le “aseaza” pe linii sau pe linii si coloane.

Dar daca am vrea sa grupam variabilele in moduri oarecare? Daca am vrea, de exemplu, sa le legam intre ele asa cum orasele dintr-o tara sunt legate prin drumuri?

Ne-ar trebui o modalitate de a “trasa drumuri” intre variabile — sau de a spune “variabila asta e legata de variabila astalalta”.

Pointerii ne permit sa facem acest lucru.

Datorita lor cresc arbori pe taramul programarii. 🙂
(Nu e o gluma. Arborii sunt o structura de date fara de care chiar nu imi imaginez cum ar fi aratat utilizarea calculatoarelor in prezent. (Probabil mai mult ne-am fi uitat la ele si le-am fi sters de praf decat sa le utilizam efectiv.))

Ce sunt pointerii?

Am spus deja ca pointerii sunt un fel de “legaturi” intre variabile. Adica printr-un pointer putem spune ca “variabila A e lagata de variabila B” sau ca “exista un drum care leaga orasul A de orasul B” sau ca “dosarul A se gaseste plasat in interiorul dosarului B”.

Deci ce e un pointer, mai precis? Uite figura asta:

Pointerul este “sageata” care arata catre variabila B.

Uau! N-a fost greu, nu? Pointerii sunt “sageti”, deci. (Nu e de mirare, atunci, ca atat de multi programatori in devenire sunt speriati de ei.) 🙂

Serios vorbesc. Asa “traiesc” pointerii in mintea mea. Daca nu i-as “vedea” sub forma grafica chiar nu stiu daca as fi in stare sa lucrez cu ei. (Caci eu nu sunt “matematician”, ci “desenator” (deci nu lucrez cu notiuni abstracte, ci cu forme concrete).)

Acum vine partea a doua: Cum “desenezi” o “sageata” intr-un program?

Ei bine… printr-o variabila! (Si uite cum iar te-am bagat in ceata…) 🙂

Cum implementezi practic un pointer?

Am zis, deci, ca un pointer este un fel de sageata care arata catre o variabila si ca el poate fi realizat practic intr-un program cu ajutorul unei (alte) variabile. Deci e cazul sa reluam (un pic mai aprofundat acum) notiunea de variabila.

Ce era o variabila?… (Iti amintesti?)

O variabila e o entitate (ca sa nu zic “chestie”) care tine in ea o valoare si are un nume.

In programul pe care il scriu ii folosesc numele, dar de fapt lucrez cu valoarea stocata in ea. De exemplu, daca am variabila a

var a;

atunci cand scriu

a = 3;

inlocuiesc valoarea care se gasea in variabila a cu valoarea 3. Iar daca apoi scriu

a = a+4;

mai intai (calculez valoarea expresiei din dreapta operatorului de atribuire, adica) calculez valoarea expresiei “a+4” (iar rezultatul este “(valoarea stocata in variabila a) plus (valoarea 4)”), adica 7 (daca tin cont de faptul ca prin instructiunea precedenta (“a = 3;”) am pus in variabila a valoarea 3) si apoi (realizez atribuirea “a = 7”, adica) in variabila a stochez valoarea rezultata prin evaluarea expresiei, adica valoarea 7; deci in urma executarii acestei instructiuni variabila numita a va avea valoarea 7.

Aceasta instructiune (“a = a+4”) este interesanta pentru ca iti arata ambele moduri in care o variabila poate fi folosita intr-un program, adica pentru:

– citire valoare (atunci cand numele variabilei apare in expresii (iar la evaluarea acestor expresii numele va fi inlocuit automat cu valoarea continuta de variabila))

– scriere valoarea (atunci cand numele variabilei apare in stanga operatorului de atribuire (iar la executarea operatiei de atribuire valoarea veche a variabilei va fi inlocuita cu noua valoare)).

Hai sa revenim. Ziceam ca o variabila e o chestie (entitate, pardon) care tine in ea o valoare si care are un nume…

Completez (in urma discutiei anterioare) aceasta definitie cu:

… pe care il pot folosi fie pentru a citi valoarea memorata in variabila, fie pentru a scrie o noua valoare in variabila.

(Sau, altfel spus, variabila este ceva care contine o valoare si care are un nume cu ajutorul caruia poate fi accesata acea valoare.)

Buuun, dar astea le stiai deja, nu?

Si cred ca mai stiai si ca variabilele sunt depozitate in memoria calculatorului. De fapt, in realitate ele sunt mici “bucatele” din memoria calculatorului.

Ceea ce probabil ca nu stiai pana acum este faptul ca memoria calculatorului seamana foarte mult cu un vector urias. Adica este formata din foarte multe bucati mici (numite locatii de memorie) asezate in linie una langa alta si care pot fi accesate prin indici.

Adica daca memoria calculatorului ar fi un vector numit mem (de 1000 de elemente, sa zicem (desi in realitate un numar de la un miliard in sus ar fi mai veridic))

var mem = Vector(1000);

atunci prima locatie de memorie ar fi mem[0], ultima locatie de memorie (adica cea de-a o mia locatie) ar fi mem[999] si orice locatie de memorie ar putea fi accesata printr-un indice, astfel: mem[i] (unde in cazul asta indicele i poate avea valori intre 0 si 999 inclusiv).

Heeei! Dar daca-i asa, asta nu inseamna ca variabilele mai au si o alta caracteristica in afara de valoare si nume — si anume indicele la care se gasesc plasate in memorie?

Pai da. Am zis ca memoria e ca un vector in care locatiile de memorie sunt elemente (deci pot fi accesate prin indici). Si am mai zis ca variabilele sunt in realitate locatii de memorie.

Deci atunci cand definesc o variabila a, ea va fi plasata in (vectorul) memorie la o anumita pozitie (adica la un anumit indice in vector). Indicele din memorie la care este plasata variabila a poarta numele de “adresa” variabilei a.

Asadar, variabila este o entitate caracterizata de:

valoarea stocata in ea (la momentul curent din executia programului)

numele (folosit pentru a-i accesa valoarea (fie pentru citire, fie pentru scriere))

– si adresa la care este plasata variabila in memoria calculatorului (care este organizata ca un vector in care elementele pot fi accesate prin indici (sau adrese)).

Oof, cam lunga discutia, nu? Probabil ai si uitat despre ce vorbeam.

Era vorba despre pointeri si despre faptul ca un pointer este o “sageata” care “arata catre” o variabila. Si ziceam ca poti implementa un pointer folosind o variabila.

Cum implementezi practic un pointer? (pe bune, acum)

Faptul ca variabila are si adresa e ca si cum ar avea un al doilea nume.

Sa zicem ca variabila a este plasata in memorie la adresa 10. Atunci ma pot referi la ea in doua moduri echivalente:

– 1) variabila numita a

– 2) variabila care se gaseste in memorie la adresa 10.

Pentru a putea vizualiza lucrurile, sa zicem ca memoria ar fi un vector mem (asa cum am discutat mai sus) si ca mai am in program inca doua variabile: variabila b, plasata la adresa 13, variabila c, plasata la adresa 7 si variabila p, plasata la adresa 2.

Adica ceva gen:

Asta inseamna ca atunci cand accesez variabila a accesez de fapt locatia de memorie de la adresa 10, adica elementul mem[10] din vectorul mem.

Similar, variabila b este de fapt elementul mem[13], variabila c este elementul mem[7], iar variabila p este elementul mem[2].

(Numele variabilelor este doar o simplificare pentru programator (caci in realitate calculatorul foloseste adresele variabilelor pentru a accesa locatiile de memorie corespunzatoare lor).)

Hei, dar era vorba despre pointeri si despre cum poti face un pointer folosind o variabila!

Uita-te, te rog, la variabila p. E o variabila ca oricare alta, doar ca vreau s-o fac pointer — adica “sageata” care sa-mi “arate catre” o alta variabila (sa zicem, catre variabila a).

Cum pot face asta? Stocand in p adresa variabilei a, adica 10. Voila !

Dar daca voiam ca pointerul p sa “arate catre” variabila b? Evident, puneam in el adresa variabilei b, adica 13.

OK. Ai vazut cum poti face ca pointerul p sa arate catre o variabila.

Dar cum poti face sa folosesti acea variabila? (Adica s-o accesezi.) Sa zicem ca vrei sa pui in variabila c valoarea variabilei “pointate” de catre pointerul p. Pai, simplu:

c = mem[p];

Daca p “pointa” catre a (deci in p era adresa variabilei a, adica 10), efectul intructiunii anterioare este “c = mem[10]” (adica “c = a”, practic). Iar daca p pointa catre b (adica in p era valoarea 13 (care este adresa lui b)), efectul instructiunii anterioare este “c = mem[13]” (adica, practic, “c = b”).

Deci asa faci citirea.

Dar daca vrei sa scrii in variabila “pointata” de catre pointerul p valoarea variabilei c? Tot simplu (daca ai inteles cum functioneaza vectorii):

mem[p] = c;

Daca p pointa catre a (deci in p era 10), efectul instructiunii e “mem[10] = c” (adica practic “a = c”). Iar daca p pointa catre b (deci in p era 13), efectul instructiunii e “mem[13] = c” (adica practic “b = c”).

In concluzie

Pointerii din programare sunt o banalitate. Dar asta e valabil doar daca ai inteles bine ce e o variabila si mai ales ce e si cum se foloseste un vector de variabile.

Deci nu pointerii sunt dificili, ci dificil e sa iti antrenezi mintea sa lucreze cu variabile nu folosindu-le numele, ci folosindu-le adresele. Adica sa nu le mai identifici folosind nume, ci sa le identifici folosind numere.

Altfel spus, daca vrei sa te imprietenesti cu pointerii, e cazul sa-ti bagi variabilele la “puscarie” si sa le vorbesti pointerilor (care sunt un fel de gardieni docili, dar tacuti, incruntati si fara simtul umorului) pe limba lor — adica nu le spune despre “detinutul” Andreea, ci despre “detinutul” din celula 137. 🙂

Javascripticul nostru limbaj de programare minimalist nu permite folosirea (explicita a) pointerilor (asa cum o permit limbaje ca C si C++), dar iti poti face “antrenamentul” foarte bine folosind un vector mem (asa cum am discutat mai sus). De exemplu, spune-mi, te rog, cat face suma variabilelor a si b in urma rularii programului urmator:

var mem = Vector(100);
// Variabila a e plasata in mem la adresa 75.
// Variabila b e plasata in mem la adresa 13.
var pointer;
pointer = 13;
mem[pointer] = 75;
pointer = pointer+62;
mem[pointer] = 13;
mem[pointer] = mem[pointer] * 100;
// Deci cat este acum a+b?…

 

Lasa-mi raspunsul intr-un comentariu mai jos. Recomand foaie si pix. (Caci daca reusesti sa inveti pointerii fara elementele astea doua, atunci eu sunt cel care are nevoie de lectii de la tine.) 🙂

 

Alatura-te celor peste 3600 de oameni din armata noastra de creiere cu muschi si vei primi testul care iti va spune daca ai sau nu minte de programator:

(nu trimit spam; te tin la curent cu noutatile)

 

 

Cu drag,

Florin





25 comments
DeaconuMihai
DeaconuMihai

mem(13)=b=75

mem(13+62)=mem(75)=a =13

mem(75)=a=mem(75)*100=a*100=1300

a+b=1300+75=1375

Ioan
Ioan

var pointer;
pointer = 13;
mem[pointer] = 75;


// Dupa pasii de mai sus variabila care se afla in celula 13, adica b ia valoarea lui mem[pointer], adica 75

pointer = pointer+62;
mem[pointer] = 13;

// Dupa acesti doi pasi variabila care se afla in celula pointer+62, adica in celula 75 (13+62), mai exact variabila a ia valoarea lui mem[pointer], adica 13 //

// Daca s-ar opri aici secventa de cod, atunci a+b= 13+75= 88, dar mai urmeaza o linie de cod:

mem[pointer] = mem[pointer] * 100;

// Prin urmare valorile inregistrate ca mem[pointer] in variabilele a si b, adica 13 si 75, se inmultesc fiecare cu 100. Mai exact valuarea a = 13*100 si b=75*100 


Concluzie: a+b=13*100 + 75*100= 1300 +7500 = 8800.


Q.E.D.

Ioan
Ioan

Raspunsul este 8800

Florin Birleanu
Florin Birleanu moderator

@Ioan Spune-mi, te rog, cum ai calculat... :-)

Ioan
Ioan

@Florin Birleanu 

var pointer;
pointer = 13;
mem[pointer] = 75;


// Dupa pasii de mai sus variabila care se afla in celula 13, adica b ia valoarea lui mem[pointer], adica 75

pointer = pointer+62;
mem[pointer] = 13;

// Dupa acesti doi pasi variabila care se afla in celula pointer+62, adica in celula 75 (13+62), mai exact variabila a ia valoarea lui mem[pointer], adica 13 //

// Daca s-ar opri aici secventa de cod, atunci a+b= 13+75= 88, dar mai urmeaza o linie de cod:

mem[pointer] = mem[pointer] * 100;

// Prin urmare valorile inregistrate ca mem[pointer] in variabilele a si b, adica 13 si 75, se inmultesc fiecare cu 100. Mai exact valuarea a = 13*100 si b=75*100 


Concluzie: a+b=13*100 + 75*100= 1300 +7500 = 8800.


Q.E.D.

Ioan
Ioan

@Florin Birleanu 

var pointer;
pointer = 13;
mem[pointer] = 75;

// Dupa pasii de mai sus variabila care se afla in celula 13, adica b ia valoarea lui mem[pointer], adica 75 //

pointer = pointer+62;
mem[pointer] = 13;

// Dupa acesti doi pasi variabila care se afla in celula pointer+62, adica in celula 75 (13+62), mai exact variabila a ia valoarea lui mem[pointer], adica 13 //

// Daca s-ar opri aici secventa de cod, atunci a+b= 13+75= 88, dar mai urmeaza o linie de cod //


mem[pointer] = mem[pointer] * 100;

// Prin urmare, valorile inregistrate ca mem[pointer] in variabilele a si b, adica 13 si 75, se inmultesc fiecare cu 100. Mai exact valuarea a = 13*100 si b=75*100 


Concluzie: a+b=13*100 + 75*100= 1300 +7500 = 8800.

Ioan
Ioan

@Florin Birleanu 

var pointer;
pointer = 13;
mem[pointer] = 75;

// Dupa pasii de mai sus variabila care se afla in celula 13, mai exact variabila b ia valoarea lui mem[pointer], adica 75 //

pointer = pointer+62;
mem[pointer] = 13;

// Dupa acesti doi pasi variabila care se afla in celula pointer+62, adica in celula 75 (13+62), mai exact variabila a ia valoarea lui mem[pointer], adica 13

Daca s-ar opri aici secventa de cod, atunci a+b= 13+75= 88, dar mai urmeaza o linie de cod //


mem[pointer] = mem[pointer] * 100;

// Prin urmare, valorile inregistrate ca mem[pointer] in variabilele a si b, adica 13 si 75, se inmultesc fiecare cu 100. Mai exact a=13*100 si b=75*100 

Concluzie: a+b=13*100 + 75*100= 1300 +7500 = 8800 //

Q.E.D.

SirbuMarian
SirbuMarian

Salut,cum pot face testul pentru a vedea daca am creier de programator?

Florin Birleanu
Florin Birleanu moderator

@SirbuMarian Iti introduci adresa de email in caseta din partea dreapta sus a site-ului si apoi apesi butonul "Ma inrolez". Vei primi un email de confirmare in care trebuie sa faci click pe link-ul de acolo. 


Dupa aceea abonarea este OK si vei primi testul in mesajul de bun venit. :-)

MihaiZ
MihaiZ

la adresa 13( adica var b) se scrie 75, la adresa 75(adica var a) se scrie 1300.

R: a+b==1375 

DianaBT
DianaBT

Salut ! Cum pot face testul pentru a vedea daca am creier de programator? Am primit confirmarea inscrierii pe site dar niciun test

Florin Birleanu
Florin Birleanu moderator

@DianaBT Buna, Diana! Trimite-mi, te rog, acest mesaj intr-un reply la mesajul de confirmare a inscrierii pe site.

RaduAdrian
RaduAdrian

Din rularea programului ne dăm seama că var a aflată la poziția 13 din memorie este 75.. iar var b aflat la pozitia 75 este 13; apoi se mai execută inca o înmulțire a poziției 75 care nu cred ca ne interesează si care devine 13*100= 1300;.. Daca ne referim strict la a+b si ne oprim acolo rezultatul este 88;.. dacă ținem cont de var b care din 13 a ajuns 1300 ar însemna a+b=75+1300=1375.. Depinde cum privim exercițiul..

Florin Birleanu
Florin Birleanu moderator

@RaduAdrian Exercitiul nu are niciun fel de ambiguitate -- deci raspunsul e unic :-). Daca intr-un program fac urmatoarele:


var x = 0;

x = 13;

x = x*100;


si acum te intreb cat este x (adica ce valoare este stocata in variabila x in urma rularii acestor 3 instructiuni), ce raspunzi? :-)

RaduAdrian
RaduAdrian

La a doua linie 0 devine 13 deci înmulțirea va putea avea loc pentru ca se evita 0.. deci x *=100(în Java înseamnă x=x*100); si in final x=1300; Nu am foaie si pix acum dar dacă îmi spui că nu este așa îl voi verifica cu Eclipse. Apropo.. funcția de editare comment pe acest site este activă doar 5 minute.. Iar pe facebook infinit. Mai jos nu am putut șterge comentariul în care scriam a+b = 1300; Poate mă ajuți.

Florin Birleanu
Florin Birleanu moderator

@RaduAdrian E OK ce zici. Dar asta nu inseamna ca a+b = 1375? :-) Mai e vreo ambiguitate?... (Nu stiu de ce se dezactiveaza functia de editare comentariu... Ti l-am sters eu -- asta voiai, nu?)

RaduAdrian
RaduAdrian

Da, mulțumesc. Poate rezolvi totusi problema comment-urilor, să le putem chiar șterge noi :-)..

Florin Birleanu
Florin Birleanu moderator

@RaduAdrian Sigur te-ai uitat si peste lectia despre variabile?... :-) (A, si inca ceva: Sigur ai folosit foaie de hartie si pix, asa cum ti-am recomandat?...)

chrisrazv
chrisrazv

Salut!

Buna explicatie dar avantajul major care are fi ?

In afara ca eliminam o abstractizare ( a, care e de fapt p(i), e egal cu "ceva")  ar mai fi ca in pointerul acela cu lungimea > 1kk cand se acceseaza o variabila b  se cauta valoare ei de la inceputul "vectorului" si daca valoare lui b se regaseste la 1kk-1 in lungimea vectorului trebuie sa se parcurga aproape tot vectorul  dar prin pointeri stocam de la inceput adresa variabilei si mai tarziu in executia programului cand avem nevoie de valoarea ei in loc sa "parcurgem" pointerul/vectorul ii spunem direct cpu-ului unde sa o gaseasca ?

Florin Birleanu
Florin Birleanu moderator

@chrisrazv Ceea ce spui in acest comentariu are o oarecare legatura cu ideea de a analiza complexitatea algoritmilor de cautare in diverse structuri de date... dar nu asta e ideea pointerilor.


Ci ideea e ca ei sunt, de exemplu, o unealta cu ajutorul careia putem construi structuri de date complexe in care cautarile sa se faca foarte rapid. (Nu am aratat in aceasta lectie cum putem folosi pointerii pentru a face asa ceva, dar probabil vor mai urma si alte lectii. :-) )

GeorgeMarin1
GeorgeMarin1

Salut !

Foarte bun articolul ! In sfarsit am inteles ce sunt pointerii. Am fost in ceata in legatura cu ei de cand am inceput facultatea !

Apreciez munca dumneavoastra !

Dangmir8
Dangmir8

@Florin Birleanu @GeorgeMarin1 Ce mai tura bura ? Un pointer e o adresa. In electronica digitala in cadrul unei memorii exista doua magistrale ( bus-uri ): o magistrala de adrese si o magistrala de date. Orice memorie, fie ea RAM sau ROM, HDD etc are doua structuri: adrese si date. La o adresa poate fi stocata o adresa sau o data. La o adresa poate fi stocata o adresa catre o alta adresa .... samd. O adresa e un registru de orice marime ( putere a lui 2 ): 8, 16, 32, 64 biti. Asta inseamna informatica, NU desene pentru copii mici. Informatica se bazeaza pe electronica digitala si nu pe desene pt. copii mici. Informaticienii sunt niste nesimtiti in sensul ca utilizeaza o infrastructura ( electronica digitala ) despre care nu stie absolut nimic, decat ca lucreaza cu 1/0. Ce e ala 1/0 nu-i intereseaza.