Sunteți pe pagina 1din 15

Capitolul 7.

Pointeri
l56
7

Pointeri


Pointerul este o variabil care contine o adres de memorie. Aceast variabil
contine adresa unei variabile. Avantaiele utilizrii pointerilor sunt:
- oIer posibilitatea de a modiIica argumentele de apelare a Iunctiilor;
- permit o alocare dinamic a memoriei;
- pot mbunttii eIicienta anumitor rutine.
Pointerii reprezint una din cele mai puternice caracteristici ale limbaiului C.
dar si periculoase. Dac pointerii nu sunt initializati corect sau dac contin valori
incorecte pot determina blocarea calculatorului. sau s conduc la erori greu de
depistat.

7.1. Variabile de tip pointer

Dac o variabil urmeaz s contin un pointer (o adres). aceasta trebuie
declarat astIel:
tip `nume;
unde:
- tip este tipul de baz al pointerului. si speciIic tipul obiectului pe care
pointerul l poate puncta (indica. pointa);
- nume este numele variabilei de tip pointer;
- ` - asterix operator de indirectare.

Tipul de baz al pointerilor deIineste tipul de baz ctre care indic acesta. De
exemplu. dac o variabil numit px contine adresa unei variabile x de tip ntreg se
spune c px pointeaz un int. Dac x se aIl n memorie la adresa 200. atunci px va
avea valoarea 200. Fie declaratia:

int px, x, y; /` px pointer la ntreg. x .y variabile de tip ntreg */

Operatorii Iolositi n cazul pointerilor sunt & si `. Operatorul &('adresa lui)
returneaz adresa de memorie a variabilei pe care o precede.
px = &x; /* px contine adresa lui x */
Operatorul * ('de la adresa) returneaz valoarea de la adresa pe care o
precede.
y=*px; /*daca px este adresa lui x. y devine egal cu x . adic cu ce e la
adresa px*/

Capitolul 7. Pointeri
l57

Este important ca variabilele de tip pointer s indice ntotdeauna corect tipul
de date. De exemplu. dac se declara un pointer de tipul int. compilatorul ntelege
c la adresa pe care o contine pointerul va Ii memorat o variabil de tip ntreg. si nu
de alt tip.

Fie urmtorul program:
# include <stdio.h>
void main(void){
int *p;
float a, b;
p = &a; /* gresit p indic ctre un Iloat. desi este pointer de tip ntreg */
b = *p;
}

Observatie. Compilatorul nu d mesai de eroare (eventual avertisment). desi
pointerului p de tip ntreg i se atribuie adresa unui Iloat (a). n Iinal lui b nu i se
atribuie valoarea lui a. Pointerul p Iiind de tip ntreg. vor Ii transIerati n b doar doi
octeti. nu toti cei patru care Iormeaz n mod normal un numr Iloat.
Valoarea unei variabile poate Ii reIerit printr-un pointer. proces numit
indirectare. Operatorul * poate Ii Iolosit n partea stng a unei instructiuni de
asignare pentru a atribui o nou valoare Iolosind un pointer.
Exemplu:
# include <stdio.h>
void main(void){
int a, *b;
a = 55; // variabila a primeste valoarea 55
b = &a; // b primeste adresa lui a
*b = 135; // asigneaz lui a valoarea 135 Iolosind pointerul b
printf( valoarea lui a = %d, a);
}
programul asigneaz lui a o valoare. indirect. Iolosind pointerul b. Programul va
aIisa :
valoarea lui a=135

Prin declararea unui pointer. se creeaz o variabil capabil s contin o
adres de memorie. Initializarea unui pointer nu are nici un sens. este o greseal.
Exemplu:

# include <stdio.h>
void main(void){
int *a;
*a = 123; // incorect a nu puncteaz nici un obiect
Capitolul 7. Pointeri
l58
}

Printr-o instructiune de atribuire unui pointer poate s i se atribuie un alt
pointer.
Exemplu:
# include <stdio.h>
void main (void)
}
int a = 1234;
int *b, *c;
b = &a;
c = b;
printf(Valoarea lui a=%d\nAdresa lui a este %p,
a,c);
}

7.2. Aritmetica pointerilor

n plus. Iat de operatorii * si &. n expresiile cu pointeri pot Ii aplicati
numai operatorii aritmetici . . - si --. Mai mult. unui pointer i pot Ii
adunate/sczute numai cantitti ntregi.
Operatiile cu pointeri sunt eIectuate relativ la tipul pointerului. De Iiecare
dat cnd un pointer este incrementat. el va puncta articolul urmtor. De exemplu.
dac avem declaratia:
int *p;
si c el contine adresa 1000. Dup executarea instructiunii:
p++;
si dac tipul int se reprezint pe 2 bytes. p va avea valoarea de adres 1002 si nu
1001.
Pointerul p va indica spre urmtorul ntreg. Acelasi lucru este valabil si
pentru decrementare. n aceleasi conditii:
p--;
determin ca p s aib valoarea de adres 998.
Dac pointerul p ar Ii de tip Iloat (tipul Iloat se reprezint pe 4 bytes.
octeti) si adresa lui p ar Ii 1000:
float *p;
dupa executia instructiunii:
p++;
pointerul p va contine valoare 1004.
n concluzie de Iiecare dat cnd este incrementat/decrementat un pointer.
el va indica spre locatia din memorie a urmtorului/predecesorului element de
acelasi tip cu tipul su de baz.
Se pot aduna/scdea ntregi la/sau din pointeri. De exemplu instructiunile:
int *a, *b;

Capitolul 7. Pointeri
l59
. . . . . . . .
b = b - 6;
a = a +5;

Iace ca pointerul a s indice al 5-lea element de acelasi tip cu a dup cel pe care l
indic n mod curent. respectiv b indic ntregul aIlat n memorie cu 6 ntregi
naintea sa.
n aIara adunrii. scderii dintre un pointer si un ntreg. incrementrii si
decrementrii. este permis diIerenta ntre 2 pointeri pentru a determina
numrul de obiecte de acelasi tip care separ cei doi pointeri.
Operatiile de incrementare/decrementare pot Ii aplicate asupra pointerului
nsusi sau asupra obiectului pe care l puncteaz. Trebuie procedat cu prudent
dac se ncearc s se incrementeze obiectul pe care l puncteaz un pointer.
De exemplu:
int p;
instructiunea:
*p++;
obtine mai nti valoarea pe care o puncteaz p si apoi p este incrementat pentru a
puncta elementul urmtor. Pentru a incrementa obiectul pe care l puncteaz p
trebuie s Iolosim instructiunea:
(*p)++;
Parantezele Iac ca valoarea punctat de p s Iie incrementat.

7.3. Compararea pointerilor

Sunt admise compararea a doi pointeri Iolosind operatorii relationali. dac
pointerii puncteaz acelasi obiect. asa cum este tabloul sau sirul de caractere.
Exemplu:
Program ce asigur utilizarea unei stive de tip LIFO (ultimul intrat primul
deservit). Functiile depune() si extrage() simuleaz instructiunile push si pop. Dac
valoarea introdus de la tastatur este diIerita de 0. valoarea se depune n stiv. n
caz contrar se extrage ultima valoare introdus. Bucla creat se termin la
introducerea valorii 1.

# include <stdio.h>
# include <stdlib>
# define DIM 100
void depune(int i); // push
int extrage(void); // pop
int *vrf, *p, stiva [DIM];
void main(void){
int val;
Capitolul 7. Pointeri
l60
vrf = stiva; // indica vrful stivei
p = stiva; // initializeaza p cu adresa stivei
do {
printf (Introduceti valoarea:);
scanf( %d, &val);
if (val ! = 0)
depune(val);
else
printf(valoarea extrasa este : %d \n,
extrage());
} while (val! = -1);
}
void depune(int i){
p ++;
if (p = = (virf + DIM)) {
printf ( Depasire stiva);
exit (1);
}
*p = i;
}
int extrage(void){
if (p = = vrf) }
printf(Stiva vida);
exit (1);
}
p - -;
return *(p + 1);
}

Variabila vrI pstreaz adresa din memorie a vrIului stivei. implementat
prin tabloul de ntregi stiv. Cu aiutorul variabilei p avem acces eIectiv la stiv. n
Iunctiile depune() si extrage() se eIectueaz un test relational asupra pointerului p
pentru a testa erorile de depsire a limitelor stivei. AstIel n Iunctia depune().
variabila p este comparat cu sIrsitul stivei adunndu-se la vrI valoarea DIM
(dimensiunea stivei). iar n Iunctia extrage(). p este comparat cu vrI pentru a se
asigura c nu a aprut o depsire inIerioar.

Observatie. n instructiunea return sunt necesare paranteze deoarece Ir ele
instructiunea ar arta astIel:
return * p + 1 ;
si n acest caz ar returna valoarea locatiei p la care s-a adugat valoarea 1 si nu
valoarea locatiei p 1.

7.4. Pointeri si tablouri


Capitolul 7. Pointeri
l6l
ntre pointeri si tablouri exist o strns legtur. ei sunt interschimbabili.
Aceast legtur Iace ca implementarea lor mpreun s Iie unic si puternic.
Dac Iolositi numele unui tablou Ir indice. generati de Iapt un pointer
ctre primul element al tabloului. Acest lucru permite ca aceast valoare s Iie
asignat unui alt pointer si astIel devine posibil accesarea elementelor tabloului
Iolosind pointerul. De exemplu:
char sir[40], *p;
p = sir;
Variabila p a Iost initializat cu adresa primului element din sir. sir|0|.
Pentru a accesa al treilea element din sir se poate scrie:
sir[2]
sau
*(p + 2)
Ambele constructii vor returna al treilea element. Elementele unui tablou
ncep de la 0. Pentru a avea acces la al treilea element. trebuie Iolosit indicele 2
pentru sir sau s adugati 2 la pointerul p. deoarece p indic eIectiv spre primul
element din sir( spre sir|0| ).
Limbaiul C oIer dou metode de acces la elementele unui tablou:
aritmetica pointerilor sau indicii tablourilor. Prima este mai rapid.
Exemplu:
# include <stdio.h>
void main(void){
int *p, a[5] = {1,2,3,4,5};
int i;
p = a; //asigneaz lui p adresa de nceput a tabloului
printf(AIisarea tabloului Iolosind indicii tabloului \n);
for (i =0; i <5; i++)
printf(a[%d] = %d \n, i, a[i] );
printf (AIisarea tabloului Iolosind pointeri. nu indici de tabel \n);
for (i = 0; i <5; i ++)
printf( a[%d] = %d \n, i, *(p + i) );
}

Ambele instructiuni printI() aIiseaz acelasi lucru. n expresia *(p i)
sunt necesare paranteze deoarece operatorul * reIeritor la pointeri are o precedent
mai mare dect operatorul .

Exemplu. Se cere un program care s citeasc de la tastatur un sir de
caractere. apoi s-l aIiseze cu litere mari (Iolosind Iunctia toupper () respectiv cu
litere mici (Iunctia tolower () ).
O prima versiune ar Ii Iolosind aceea n care pentru a accesa caracterele
din sir se indexeaz numele tabloului. iar alta ar Ii Iolosirea unui pointer pentru a
accesa caracterele sirului.
Capitolul 7. Pointeri
l62
# include <stdio.h>
# include <ctype.h>
void main(void){
char sir [40], *p;
int i;
printf(Introduceti sirul : \n);
gets (sir);
for (i = 0; sir[i]; i++) //ultimul caracter din sir este /0
sir[i] = toupper[i];
printf ( %s \n, sir); // aIisare cu litere mari prima variant

p = sir; // p pointeaz primul element din sir
while (*p) {
*p = toupper(p);
p++;
}
printf(%s \n,sir); // aIisare cu litere mari varianta 2

for (i = 0; sir[i]; i++)
sir[i] = tolower (sir[i]);
printf ( %s \n , sir); // aIisare cu litere mici prima variant
p = sir; // resetarea pointerului p
while (*p) {
*p = tolower(p);
p++;
}
printf ( %s \n , sir); //aIisare cu litere mici a 2-a variant
}

Instructiunea:
while (*p) {
*p = tolower(p);
p++;
}
poate Ii scris si astIel:
while (*p)
p++ = tolower (*p);

Asemntor modului n care se incrementeaz un pointer se poate
decrementa un pointer.

Exemplu. Program care copiaz continutul unui sir ntr-un alt sir n ordine invers.

# include <stdio.h>
# include <string.h>
void main(void){

Capitolul 7. Pointeri
l63
char sir1 = Acesta este un sir;
char sir2 [40], * p1, *p2;
p1=sir1 + strlen(sir1)-1; // p1 pointeaz sIrsitul sirului sir1
p2 = sir2;
while (p1 >=sir1)
*p2 ++ = * p1-- ;
*p2 = `\0'; // caracterul null ncheie sirul sir2
printf ( %s \n %s, sir1, sir2);
}
Pointerul p1 puncteaz sIrsitul sirului sir1. iar pointerul p2 nceputul
sirului sir2. Compararea pointerilor n bucla while. asigur oprirea procesului de
copiere atunci cnd este atins nceputul sirului sir1.

7.5. Pointeri si siruri initializate (Initializarea
pointerilor)

La ntlnirea unui sir initializat. compilatorul depune sirul n tabela de
siruri a programului si genereaz un pointer ctre el.
# include <stdio.h>
void main(void){
char *p, sir[] = Acesta este un sir;
p = sir;
printf(p);
p = Acesta este un sir;
printf(p);
}
Variabila p este declarat ca pointer de tip char. deci el poate puncta un
tablou de caractere. La compilarea liniei:
p = Acesta este un sir;
compilatorul depune sirul n tabloul de siruri al programului si asigneaz lui p
adresa sirului n tablou.
Programul poate Ii rescris astIel:
# include <stdio.h>
void main(void){
char *p = Acesta este un sir;
printf(p);
}
Variabila p este initializat s puncteze un sir.

Exemplu. Program care citeste siruri de caractere pn se introduce parola corect.

# include <stdio.h>
# include <string.h>
Capitolul 7. Pointeri
l64
char *p = parola;
void main(void)
{
char str[40];
do {
printf (Introduceti parola corect \n);
gets(s);
} while (strcmp(p, sir)) ;
}

7.6. Tablouri de pointeri

Pointerii pot Ii organizati sub Iorma de tablouri ca oricare alt tip de date.
Declararea unui tablou de pointeri de tip int de 10 elemente este:
int *x[10];
Pentru a atribui adresa unei variabile de tip ntreg cu numele num
elementului al doilea al tabloului de pointeri x. se va scrie:
x[1] = &num;
iar pentru a obtine valoarea num se va scrie:
*x|1|.
Tablourile de pointeri sunt Iolosite de obicei pentru a pstra pointeri ctre
siruri de caractere.
Exemplu. Functia eroare( ) aIiseaz un mesai de eroare pe baza valorii
parametrului numerr.
char * p[ ] = {
Lipseste ; ,
Lipseste ( ,
Lipseste ) ,
Lipseste { ,
Lipseste }
};
void eroare(int num-err){
printf(p[num_err]);//echivalent cu printf(%s,p[num-err]);
}

Pointerul p pstreaz pointeri ctre Iiecare sir. De exemplu dac num err
2 se va aIisa:
Lipseste )

Exemplu. Program ce Ioloseste un tablou bidimensional de pointeri pentru
a crea un tabel de siruri care leag diIerite obiecte de locul n care se aIl.
# include <stdio.h>
# include <sting.h>
char *p[][2]={
cartea, n biblioteca,

Capitolul 7. Pointeri
l65
creion, pe masa,
pix, n penar,
guma, sub masa,
, //sirul null ncheie tabela
}
void main(void){
int i;
char obiectul[40];
printf(Introduceti numele obiectului cautat: );
gets(obiectul);
for (i = 0; *p[i][0]; i++) {
if (! strcmp(obiectul, p[i][0]))
printf ( %s este %s \n, obiectul, p[i][1];
}
}

Capitolul 7. Pointeri
l66
7.7. Indirectare multipl

Este posibil s avem un pointer care s puncteze un alt pointer. Aceast
situatie este denumit indirectare multipl sau pointer la (ctre) pointer.
Pointer Pointer Variabil



Indirectare multipl

Cnd un pointer puncteaz un alt pointer. primul pointer contine adresa
celui de al doilea pointer care puncteaz locatia ce contine obiectul. valoarea
dorit.
Pentru a declara un pointer ctre un alt pointer. trebuie plasat un asterix
suplimentar n Iata numelui pointerului.
De exemplu:
char ** pp;
pp este un pointer ctre un alt pointer de tip char. si nu un pointer ctre un caracter.
Accesarea valorii printr-un pointer ctre un alt pointer. cere ca operatorul
asterix s Iie aplicat de dou ori. De exemplu:
p &ch; // asigneaz lui p adresa lui ch
pp &p; //asigneaz lui pp adresa lui p
**pp c`; // asigneaz lui ch valoarea c` Iolosind indirectare multipl

Observatie. Pointerii ctre pointeri pot produce conIuzii. De aceea.
indirectarea excesiv este derutant si surs de erori conceptuale.


7.8. Pointeri la functii

Un pointer la o Iunctie este o variabil care contine adresa punctului de
intrare al Iunctiei. n urma compilrii si link-editrii Iiecare punct de intrare are o
adres Iizic. adres care este Iolosit de Iiecare dat cnd Iunctia este reIerit
(apelat). Deci este normal s avem un pointer care s o puncteze. Folosind un
pointer care puncteaz o Iunctie. este posibil s apelm Iunctia Iolosind pointerul.
Pentru a crea o variabil care s poat puncta( indica) o Iunctie. trebuie
declarat un pointer de acelasi tip cu tipul Iunctiei. urmat de parametrii Iunctiei. De
exemplu. declaratia:

float (*pf)(float x, floar y);
Adresa Adresa Valoare

Capitolul 7. Pointeri
l67
stabileste c pI este un pointer la o Iunctie care returneaz un Iloat si are 2
parametrii x si y de tip Iloat.

Observatie. Este necesar ca. constructia *pI s Iie inclus ntre paranteze rotunde
din cauza regulilor de pecedent a operatorilor.

Pentru a asigura unui pointer adresa unei Iunctii. trebuie Iolosit numele
Iunctiei Ir paranteze.
Fie prototipul unei Iunctii:
float dif (float x, float y);
si pointerul pI declarat mai sus. instructiunea
pf = dif;
este corect. n urma acestei asignri. Iunctia diI ( ) se poate apela indirect prin pI.
prin instructiunea de Iorma:
reg = (*pf)(3.5, - 4.5);

Programul complet pentru acest exemplu este:
# include <stdio.h>
float dif (float x, float y);
void main(void){
float (*pf) (float x, float y);
float rez;
pf = dif; // adresa functiei se atribuie lui pf
rez = (*pf) (3.5, - 4.5);
printf(rezult = %lf \n , rez);
}

Una din cele mai importante utilizri ale pointerilor la Iunctii apare atunci
cnd este creat un tablou de pointeri la Iunctii. Iiecare element din tablou pointeaz
la o alt Iunctie.

Pentru a apela o anumit Iunctie va trebui s indexm tabloul. Exemplu:
# include <stdio.h>
int sum(int a, int b);
int dif(int a, int b);
int mul(int a, int b);
int div(int a, int b);
int (* p[4]) (int a, int b); //
void main(void){
int rez;
int i, j, op;
p[0] = sum; // adresa functiei sum ( ) // n
p[1] = dif; // adresa functiei dif ( ) // n
p[2] = mul; // adresa functiei mul ( ) // n
Capitolul 7. Pointeri
l68
p[3] = div; // adresa functiei div ( ) // n
printf(Dati doua numere: );
scanf( %d %d , &i, &j);
printf( Optiuni:0-adunare,1-scadere,2-nmultire,3-
impartire \n);
do {
printf( Introduceti numarul optiunii:);
scanf( %d , &op);
} while (op < 0 || op >3);
rez = (* p[op])(i,j);
printf ( %d, rez);
}
int sum(int a, int b)
{
return a + b;
}
int dif(int a, int b);
}
return a - b;
}
int mul(int a, int b);
}
return a * b ;
{
int div(int a, int b);
{
if (b) return a/b;
else return 0;
}

Programul cere utilizatorului introducerea a dou numere i si i si op
numrul operatiei. Acest numr este Iolosit pentru a indica tabloul de pointeri la
Iunctii. astIel nct s se apeleze indirect Iunctia dorit.
Sigur c programul putea Ii realizat cu instructiunea switch. dar Iolosirea
tabloului de pointeri la Iunctii este mai eIicient.
Tabloul de pointeri la Iunctii poate Ii initializat ca orice alt tablou deci
programul poate Ii rescris astIel:
- - - - - -
int (* p|4| ) (int a. int b) sum. diI. mul. div};


Capitolul 7. Pointeri
l69
7.9. Folosirea pointerilor ca parametri

Parametrii transIerati unei Iunctii C pot Ii valori sau adrese. Primul tip
numit si apel prin valoare. copiaz valoarea unui argument (parametru) Iormal al
Iunctiei. ModiIicrile eIectuate asupra parametrului nu au eIect asupra
argumentului.
Al doilea mod de transIer de argumente ctre Iunctii este apelul prin
reIerint (prin adres). n acest caz este copiat adresa unui argument. n interiorul
Iunctiei adresa este Iolosit pentru acces la argumentul Iolosit eIectiv la apelare.
deci modiIicrile asupra argumentului l aIecteaz. Adresa argumentului nu este
altceva dect un pointer.

Exemplul 1. Program care apeleaz o Iunctie ce inverseaz ntre ele valorile a dou
variabile de tip Iloat indicate de ctre argumentele sale.
# include <stdio.h>
void inverseaza(float *x, float *y);
void main(void){
float a = 12.55, b = -75.66;
inverseaza (&a,&b); // paseaza adresele lui a si b
printf (a = %f b = % f \n, a, b):
}
void inverseaza(float *x, float *y)
{
float temp;
temp = *x;
* x = * y;
* y = temp;
}
Dac un tablou este Iolosit ca argument al unei Iunctii. acesteia i este
pasata adresa tabloului. Instructiunile Iunctiei apeleaz asupra continutului eIectiv
al tabloului si pot s-l modiIice.

Exemplul 2. Functia maiuscule ( ) aIiseaz caracterele unui argument de tip sir cu
maiuscule
# include <stdio.h>
# include <ctype.h>
void majuscule (char *sir);
void main(void){
char s[40];
gets(s)
majuscule(s);
}
void majuscule(char *sir)
Capitolul 7. Pointeri
l70
{
int t;
for (t = 0; sir [t]; t ++) {
sir[t] = toupper(sir[t] );
printf ( %c, sir[t]) ;
}

S-ar putea să vă placă și