Documente Academic
Documente Profesional
Documente Cultură
POINTERI
Un pointer este o variabil care conine o adres de memorie.
Pointerii sunt foarte mult utilizai n C pe de o parte pentru c uneori sunt singura cale de rezolvare a unei anumite
probleme, iar pe de alt parte pentru c folosirea lor duce la alctuirea unui cod mai compact i mai eficient.
Ca metod, pointerii se utilizeaz pentru un plus de simplitate.
Construcii ca &(x+1) i &3 sunt interzise. Este de asemenea interzis pstrarea adresei unei variabile registru.
Operatorul unar * trateaz operandul su ca o adres, acceseaz aceast adres i i obine coninutul.
Astfel, dac y este tot un int
y = *px
asigneaz lui y, ori de cte ori este cazul, coninutul locaiei unde pointeaz px.
Astfel secvena
px = &x;
y = *px;
asigneaz lui y aceiai valoare ca i
y = x
Totodat este necesar declararea variabilelor care apar n secvena:
int x, y;
int *px;
Declararea lui x i y este deja cunoscut. *px este un int, adic n momentul n care px apare n context sub forma *px,
este echivalent cu a ntlni o variabil de tip int. De fapt, sintaxa declararii unei variabile imit sintaxa expresiilor n care
ar putea s apar respectiva variabil. Acest raionament este util n toate cazurile care implic declaraii complicate.
Exemple:
double atof(), *dp; //atof() i *dp au valoare de tip double.
De notat declaraia implicit, ceea ce vrea s nsemne c un pointer este constrns s pointeze o anumit categorie de
obiecte (funcie de tipul obiectului pointat).
Pointerii pot apare n expresii.
De exemplu, dac px pointeaz pe ntregul x atunci *px poate apare n orice context n care ar putea apare x.
y = *px + 1
d lui y o valoare egal cu x plus 1.
printf("%d\n", *px)
imprim o valoare curent a lui x i d = sqrt((double) *px)face ca d = radical din x, care este forat de tipul
double nainte de a fi transmis lui sqrt.
n expresii ca
y = *px + 1
operatorii unari * i & au prioritate mai mare dect cei aritmetici, astfel aceast expresie, ori de cte ori pointerul px
avanseaz, adun 1 i asigneaz valoarea lui y.
Referiri prin pointer pot apare i n partea stng a asignrilor. Daca px pointeaz pe x atunci
*px = 0
l pune pe x pe zero i
*px += 1
l incrementeaz pe x, ca i
(*px)++
In acest ultim exemplu parantezele sunt necesare; fr ele, expresia va incrementa pe px n loc s incrementeze ceea ce
pointeaz px deoarece operatorii unari * i + sunt evaluai de la dreapta la stnga.
Dac pointerii sunt variabile, ei pot fi manipulai ca orice alt variabil. Daca py este un alt pointer pe int, atunci
py = px
copiaz coninutul lui px n py fcnd astfel ca py s se modifice odat cu px.
O mare atenie trebuie acordat tipului variabilei spre care pointeaz un pointer. Urmtorul exemplu este sugestiv n acest
sens:
int *p;
double x=1.23, y;
p=&x;
y=*p;
//valoarea lui y va fi total eronat datorit tipului pointerului p
//p este pointer spre ntreg; *p va lua din x doar primii 4 octeti si apoi ii va converti la int
// valoarea *p va fi convertita la double si salvata in y
//atentie : nu rezulta eroare de compilare, se da doar un warning
2
Instructiunea
p + n
desemneaza al n-lea obiect din memorie dupa cel pointat curent de p. Acest lucru este adevarat indiferent de tipul obiectelor
pe care p a fost declarat ca pointer. Compilatorul atunci cind il intilneste pe n, il decaleaza n functie de lungimea obiectelor
pe care pointeaza p, lungime determinata prin declaratia lui p (sizeof(*p)).
Este valid i scderea pointerilor: dac p i q pointeaz pe elementele aceluiai tablou, p-q este numrul de elemente
dintre p i q. Acest fapt poate fi utilizat pentru a scrie o nou versiune a lui strlen.
int strlen(char *s)//returneaz lungimea sirului
{
char *p = s;
while (*p != '\0')
p++;
return(p-s);
}
Prin declarare, p este initializat pe s, adic s pointeze pe primul caracter din s. In cadrul buclei while este examinat fiecare
caracter pn se ntlnete \0 care semnific sfiritul sirului de caractere iar apoi se scad cele 2 adrese.
Este posibila omiterea testului expilcit iar astfel de bucle sint scrise adesea
while (*p)
p++;
Deoarece p pointeaza pe caractere, p++ face ca p sa avanseze de fiecare data pe caracterul urmator, iar p-v da numarul de
caractere parcurse, adica lungimea sirului. Aritmetica pointerilor este consistenta: daca am fi lucrat cu float care ocupa
mai multa memorie decit char, i daca p ar fi un pointer pe float, p++ ar avansa pe urmatorul float.
Toate manipularile de pointeri iau automat n considerare lungimea obiectului pointat n asa fel nct trebuie s nu fie
alterat.
Operaii permise : adunarea sau scaderea unui pointer cu un intreg, scaderea sau compararea a doi pointeri.
Nu este permisa adunarea, impartirea, deplasarea logica, sau adunarea unui float sau double la pointer.
#include<stdio.h>
void schimbare(int x, int y)
//se vor crea copii ale variabilelor x i y
{ int tmp;
tmp=x;
x=y;
y=tmp;
//n cadrul copiilor variabilelor se face inversarea
}
//la revenire copiile variabilelor x i y se distrug
int main()
{ int x=5, y=7;
schimbare(x,y);
//transmitere prin valoare
printf(%d %d\n,x,y);//valorile rmn nemodificate adic se va afia 5 7
}
Pentru o funcie, argumentele care sunt de tipul Input-output sau doar output trebuiesc transmise (obligatoriu) prin
adres.
n cazul n care apar masive, avnd n vedere c numele tabloului este un pointer constant spre primul element al lui, se
folosete numele masivului ca adres de nceput. Elementele masivului nu vor fi copiate iar operaiile se vor face astfel
asupra zonei originale.
O utilizare comun a argumentelor de tip pointer (transmitere prin adres) se ntlnete n cadrul funciilor care trebuie s
returneze mai mult dect o singur valoare. De exemplu, constatm faptul c funcia scanf utilizeaz transmiterea prin
adres, datorit faptului c, in argumentele sale, trebuie s regsim valorile care se citesc.
5
Daca pa pointeaz pe un element oarecare al lui a atunci prin definiie pa+1 pointeaza pe elementul urmator i n general
pa-i pointeaza cu i elemente inaintea elementului pointat de pa iar pa+i pointeaza cu i elemente dupa elementul
pointat de pa.
Astfel, dac pa pointeaz pe a[0]
*(pa + 1)
refera coninutul lui a[1],
Aceste observaii sunt adevrate indiferent de tipul variabilelor din tabloul a. Definiia "adunrii unitii la un pointer " i
prin extensie, toat aritmetica pointerilor este de fapt calcularea prin lungimea n memorie a variabilei pointat. Astfel, n
pa+i, i este nmulit cu lungimea variabilei pe care pointeaz pa (sizeof(*pa)) nainte de a fi adunate la pa.
Corespondena ntre indexare i aritmetica pointerilor este evident foarte strns. De fapt, numele unui tablou este convertit
de ctre compilator ntr-un pointer constant pe nceputul zonei de memorie unde se afl tabloul.
Efectul este c numele unui tablou este o expresie pointer.
Aceasta are cteva implicaii utile. Din moment ce numele unui tablou este sinonim cu locaia elementului su zero,
asignarea
pa = &a[0]
poate fi scris i pa = a
O referin la a[i] poate fi scris i ca *(a+i).Evalund pe a[i], C l convertete n *(a+i); cele dou forme sunt
echivalente. Aplicnd operatorul & ambilor termeni ai acestei echivalene, rezult ca &a[i] este identic cu a+i: a+i adresa
elementului al i-lea n tabloul a.
Reciproc, dac pa este un pointer el poate fi utilizat n expresii cu un indice; pa[i] este identic cu *(pa+i).
Pe scurt orice tablou i exprimare de indice pot fi scrise ca un pointer i deplasament i orice adres chiar n aceeai
instruciune. Trebuie inut seama de o diferen ce exist ntre numele tablou i un pointer. Un pointer este o variabil,
astfel ca pa=a i pa++ sunt operaii.
Dar, un nume de tablou este o constant, de aceea construcii ca a=pa sau a++ sunt interzise.
Atunci cnd se transmite un nume de tablou unei funcii, ceea ce se transmite este locaia de inceput a tabloului. In cadrul
funciei apelate acest fapt argument este o variabil ca oricare alta astfel ncat un argument nume de tablou este un veritabil
pointer, adica o variabila continind o adresa.
Ne vom putea folosi de aceasta pentru a scrie o nou versiune a lui strlen, care calculeaza lungimea unui sir.
int strlen(char *s) // returneaz lungimea sirului s
{
int n;
char *ps;
for (n = 0, ps = s; *ps != '0'; ps++)
n++;
return n;
}
Ca parametri formali n definirea unei functii
char s[]
i
char *s;
sunt echivalenti; alegerea celui care trebuie scris este determinata n mare parte de expresiile ce vor fi scrise n cadrul
functiei. Atunci cind un nume de tablou este transmis unei functii, aceasta poate, dupa necesitati s-o interpreteze ca
tablou sau ca pointer i sa-l manipuleze n consecinta. Functia poate efectua chiar ambele tipuri de operatii daca i se pare
potrivit i corect.
Este posibil i transmiterea ctre o funcie doar a unei pri dintr-un tablou prin transmiterea unui pointer pe nceputul
subtabloului. De exemplu, dac a este un tablou;
f(&a[2])
si
f(a + 2)
ambele transmit functiei f adresa elementului a[2] deoarece &a[2] i a+2 sint expresii pointer care refera al treilea element
al lui a. n cadrul lui f, declarea argumentului poate citi
f(int arr[])
{
...
}
7
sau
f(int *arr)
{
...
}
Astfel, dupa cum a fost conceput funcia f aptul c argumentul refer de fapt o parte a unui tablou mai mare, nu are
consecine.
Deoarece argumentele sunt transmise prin valoare (atentie: avem pointeri transmisi prin valoare), mystrcpy poate utiliza s
i t n orice fel se dorete. Aici ei sunt convenional utilizati ca pointeri, care parcurg tablourile pina n momentul n care
s-a copiat \0 sfirsitul lui t, n s.
In practic, mystrcpy nu va fi scris asa cum s-a aratat mai sus. O a doua posilitate ar fi
int mystrcpy(char s[], char t[]) //copiaza t n s
{
int i = 0;
while ((*s++ = *t++) != '\0') i++;
return i;
}
In aceasta ultim versiune se imit incrementarea lui s i t n partea de test. Valoarea lui *t++ este caracterul pe care a
pointat inainte ca t sa fi fost incrementat; prefixul ++ nu-l schimba pe t inainte ca acest caracter sa fi fost adus.nacelasi
fel, caracterul este stocat n vede a pozitie s inainte ca s sa fie incrementat. Acest caracter este deasemenea valoarea care
se grupeaza cu \0 pentru simboul buclei. Efectul net este ca, caracterele sint copiate din t n s, inclusiv sfirsitul lui \0.
Ca o ultima abreviere vom observa ca i gruparea cu \0 este redundant, astfel c bucla while poate fi scris:
while (*s++ = *t++)
Desi aceasta versiune poate prea complicata la prima vedere, aranjamentul ca notatie este considerat suveran daca nu
exista alte raiuni de a schimba.
Rutina este mystrcmp(s, t),compara sirurile de caractere s i t i returneaza negativ, zero sau pozitiv n functie de
relatia dintre s i t; care poate fi: s<t, s=t sau s>t.
Valoarea returnat este obinut prin scderea caracterului de pe prima poziie unde s difer de t.
int mystrcmp(char s[], char t[])
{
int i;
i = 0;
while (s[i] == t[i])
if (s[i++] == '\0')
return(0);
return(s[i] - t[i]);
}
Dac ++ i -- sunt folosii altfel dect operatori prefix sau postfix pot apare alte combinaii de * i ++ i --, dei mai puin
frecvente.
Exemplu: *++p incrementeaza pe p inainte de a aduce caracterul pe care pointeaza p.
*--p decrementeaz pe p n acelasi condiii.
La lucrul cu siruri de caractere, se prefer iterarea folosind pointeri de tipul char*, si nu indeci pe sir.
10