Sunteți pe pagina 1din 5

Ce este un pointer?

(o variabil de tip pointer) (recapitulare L9)


Un pointer este o variabil care are ca valoare o adres de memorie. Spunem c variabila pointer
indic (pointeaz spre) locaia de memorie care ncepe la adresa respectiv. Acea locaie de
memorie va fi accesat n mod diferit, n funcie de cum am declarat variabila pointer.
O variabil de tip pointer se declar astfel:
tip * var;
s-ar traduce prin: valoarea spre care pointeaz var este de tipul tip
Variabila var este de tipul pointer la tip, adic tip *
De exemplu,
char *c;
va putea memora adresa unei variabile de tipul char, sau a unui tablou de elemente de tipul char
altfel spus, putem memora in c adresa unei zone de memorie n care se stocheaz unul sau mai
multe caractere.
Operatorul * se numete operator de derefereniere, deoarece, aplicat unei referine (adrese) ne d
valoarea stocat la acea adres. Se poate aplica doar unei adrese (pointer).
Cu ajutorul pointerilor putem s manevrm diverse variabile (aceste variabile se pot declara staticcum am facut pn acum, sau dinamic-este prezentat mai jos); dar nu conin informaie efectiv.
De aceea trebuie s ne asigurm c un anumit pointer este ataat la o variabil, nu putem avea:
char *c;
*c=100;
Un pointer poate s indice doar spre o anumit variabil (zon de memorie) la un moment, se poate
schimba oricnd variabila spre care indic pointerul. Mai muli pointeri pot s indice spre o aceeai
variabil la un moment dat.
Dac nu v sunt foarte clare aceste aspecte, am facut mai jos o anologie foarte practic :)
O variabil obinuit e ca o tigaie (sunt tigai la care coada e detaabil) i un pointer este doar coada
cu care putem s prindem o anumit tigaie sau alta; ne putem imagina tigi far nici o coad(i deci
nu mai putem s le folosim), sau tigi cu mai multe cozi (putem avea acces la continutul lor din mai
multe pri).
De exemplu:
int tigaia1, tigaia2; // implicit orice variabil declarat are o coad "lipit"
int *coadaA; // crem o coad
coadaA=&tigaia1; // atam coada la tigaia1; de acum tigaia1 va avea 2 cozi deci o putem accesa
tigaia1 = 10; // fie direct;
(traducere: pune 10 cartofi in tigaia1)
*coadaA = 10; // fie prin intermediul cozii ataate; (traducere: pune 10 cartofi n tigaia de care
am ataat coadaA, adic tigaia1)
// ulterior putem s atam coadaA de tigaia2, ceea ce nseamn c nu mai e ataat de tigaia1:
coadaA=&tigaia2;
*coadaA=4; // echiv cu tigaia2=4;
int *coadaB; // mai crem o coad
coadaB=&tigaia2; // n acest moment tigaia 2 are 3 cozi, cea implicit, coadaA i coadaB
i ca s ncheiem analogia cu tigaia i coada, coada trebuie s fie conceput ca s mnuiasc un
anumit tip de tigaie (dac avem o coad prea mic nu poate susine greutatea tigii, dac e prea
mare n comparaie cu tigaia, nu mai avem precizie la mavrarea ei).
i foarte important!!! ca s atam unei tigi o coad, trebuie ca tigaia s existe.
int *coada;
*coada=10; // coada nu e ataat la nici o tigaie, deci nu putem avea acces la coninutul tigii
ataate de coad; logic, nu?

Transmiterea pointerilor ca parametru pentru a modifica variabila


(interschimbarea a 2 numere)
Poate v punei ntrebarea la ce ne folosesc pointerii. Sunt mai multe situaii n care pointerii
reprezint singura soluie (dei nu trebuie s uitm c pointerii ne fac viaa mai grea i deci nu o s
i folosim dect atunci cnd ne sunt neprat necesari).
O astfel de situaie este scrierea unei funcii care interschimb 2 variabile date ca parametru.
S ncercm s scriem o astfel de funcie fr pointeri:
void interschimb(int par_formal_a, int par_formal_b)
{
int aux;
aux= par_formal_a;
par_formal_a = par_formal_b;
par_formal_b =aux;
}
void main(void)
{
int a, b;
printf(a=);
scanf(%d,&a);
// citm pe a
printf(b=);
scanf(%d,&b);
// citim pe b
interschimb(a,b);
// apelam functia care ar trebui sa interschimbe
valorile variabilelor a si b ; a si b sunt parametri actuali ai functiei
printf(a=%d, b=%d, a, b);
// observam ca a si b au tot vechile valori.
}

Funcia nu a fcut ce ne ateptam noi datorit mecanismului de tranmitere a parametrilor. n


limbajul C, parametrii se transmit prin valoare, nu prin adres. De fapt, la apelul unei funcii se
aloc spaiu pe stiv pentru toate variabilele locale i pentru parametri (se numesc parametri
formali, deoarece precizeaz forma, adic tipul variabilei). n spaiul alocat pentru parametri se va
copia valoarea parametrilor actuali (adic valorile efective cu care se apeleaz funcia). Orice
modificare fcut asupra parametrilor nseamn modificarea unor valori de pe stiv; cnd se va
termina funcia, variabilele de pe stiv sunt terse (nu sunt terse efectiv, dar noi nu mai avem acces
la ele). De accea, dac vrem s modificm ntr-o funcie o variabil, nu vom transmite variabila ca
parametru, ci adresa ei. (Desigur aici nu intr n discuie variabilele globale, care sunt vizibile din
orice funcie i nu e necesar s le transmitem ca parametri.)
Programul nostru se transforma astfel:
void interschimb(int *adresa_a, int *adresa_b)
{
int aux;
aux= *adresa_a;
// in aux punem valoarea de la adresa lui a, adica pe a
*adresa _a = *adresa _b; // valoarea de la adresa lui b este copiata peste
valoarea de la adresa lui a
*adresa _b =aux; // valoarea de la adresa lui b este acum cea stocata in aux
}
void main(void)
{
int a, b;
printf(a=);
scanf(%d,&a);
printf(b=);

// citm pe a

scanf(%d,&b);
// citim pe b
interschimb(&a,&b);
// apelam functia care interschimba valorile
variabilelor a si b ; &a si &b sunt parametri actuali ai functiei
printf(a=%d, b=%d, a, b);
// observam ca a si b sunt interschimbate
}

Atenie!!! S nu cdei n extrema de a folosi pointeri tot timpul. O greeal frecvent care poate s
apar este urmtoare:
void interschimb(int *adresa_a, int *adresa_b)
{
int *aux;
*aux= *adresa_a;
*adresa _a = *adresa _b;
*adresa _b =*aux;
}
Greeala este ca *aux nu exist, doar aux; deci este total greit. n aux se
afl o valoare aleatoare; prin *aux noi mergem la acea adres aleatoare, care ar
putea fi o adres la care noi nu avem acces, sau chiar adresa unei variabile din
program pe care astfel o modificm fr s fim contieni.

Un caz aparte l reprezint tablourile. Numele unei variabile tablou reprezint de fapt adresa de
nceput a variabilei. Deci cnd vom transmite ca parametru un tablou, vom transmite de fapt adresa
tabloului, care se va copia local pe stiv. Orice acces la elementele tabloului se va face direct pe
tabloul dat ca parametru, ntruct local se copiz adresa tabloului i nu tot tabloul. Deci dac vom
dori s modificm ntr-o funcie elementele unui tablou, vom da ca parametru tabloul.

Parcurgerea unui tablou cu un pointer (recapitulare L9)


Am vzut c putem parcurge un tablou cu operatorul [] sau cu ajutorul operaiilor cu pointeri. Ne
reamintim c putem s adunm un scalar (i) la adresa unui tablou (tab). Acest scalar reprezint
peste cte elemente vrem s srim. Practic, la adresa tabloului se vor aduna i*sizeof(tab[0]) octei.
Pentru a afla valoarea de la adresa respectiv, folosim operatorul de derefereniere *.
tab+i
-> adresa elementului i din tabloul tab
*(tab+i) -> valoarea elementului i din tabloul tab
int tab[N], i;
for( i=0; i<N; i++ )
for( i=0; i<N; i++ )

tab[i]=i;
*(tab+i)=i;

Cele 2 moduri de accesare a elementelor tabloului sunt absolut identice.


Pentru o matrice vom avea: // a se vedea problema rezolvat de la L12
int tab[N][M], i;
for( i=0; i<N; i++ )
for( i=0; i<N; i++ )
tab[i][j]=i;
// sau
// *( *(tab+i)+j)=i;

Alocarea dinamica: avantaje, principiu, malloc(), calloc()


O problem la alocarea de tablouri era mrimea acestora. Dac alocm puine elemente, exist
riscul s depim memoria rezervat, dac alocm foarte multe, probabil c nu le vom folosi.
Anumite probleme nu presupun o variaie foarte mare a numrului de elemente a tabloului (de
exemplu un tablou cu studenii dintr-o grup va avea o variaie foarte mic i un numr maxim

destul de uor de apreciat, limitat de capacitatea fizic a celei mai mari sli de seminar). ntr-un
asemenea caz, vom declara variabilele n mod static, ca i pn acum. Dar pentru anumite
probleme, unde numrul de elemente al unui tablou poate s fie foarte diferit de la o rulare la alta,
putem s alocm memorie pentru elementele tabloului n timp ce programul rulez, cnd putem afla
de la utilizator cte elemente dorete s aib tabloul. Ceea ce nseamn c vom ocupa exact atta
memorie ct avem nevoie.
Singurul element sintactic de noutatea l constituie folosire funciei:

void *calloc(size_t nitems, size_t size);


Funcia aloc n heap(o zon special de memorie) un bloc de dimensiune size*nitems, pe care i
pune pe 0; dac operaia reuete returneaz un pointer la blocul alocat, altfel returneaz NULL.
Este posibil ca cererea de alocare s nu poat fi satisfcut dac nu exist un bloc compact de
dimensiune cel puin egal cu size*nitems. Dimensiunea size se specific n octei.
Rezultatul funciei calloc fiind un pointer de tip void, se va folosi operatorul cast pentru conversii
de tip la atribuire.
Ex. alocarea dinamic de memorie pentru un tablou de n numere ntregi.
#include <stdio.h>
#include <stdlib.h>
int *citire(int *adr_n);
void afisare(int *tab, n);
void main(void)
{
int n;
int *tab;
tab=citire(&n);
// .... restul programului
afisare(tab, n);
}
int *citire(int *adr_n)
{
int * tab; // tab e de tipul int *
int i;
printf("Introduceti numarul de elemente: \n");
scanf("%d", adr_n);
if ((tab=(int *)calloc(*adr_n,sizeof(int)))==NULL ) // cast la tipul int *
{ // alocam memorie pentru n intregi si testam daca s-a reusit alocarea
printf("Eroare alocare dinamica memorie !\n");
exit(1);
}
for (i=0; i<*adr_n; i++) //citim elementele ca pentru un tablou alocat
static
{
printf("tab[%d]=", i);
scanf("%d", &tab[i]);
// alternativ se poate folosi si scanf("%d", tab+i);
}
return tab; //se returneaza adresa tabloului
}
void afisare(int *tab, n)
{
for (i=0; i<n; i++)
//afisam elementele ca pentru un tablou alocat
static
{
printf("tab[%d]=", i);
scanf("%d", tab[i]);
// alternativ se poate folosi si scanf("%d", *(tab+i));

Funcia calloc este intuitiv folosit pentru tablouri unidimensionale, sau dac vrem s iniializm
memoria cu 0. O funcie pentru alocare dinamic mai general este

void *malloc(size_t size);


Funcia aloc n heap un bloc de dimensiune size; dac operaia reuete returneaz un pointer la
blocul alocat, altfel returneaz NULL. Este posibil ca cererea de alocare s nu poat fi satisfcut
dac nu mai exist suficient memorie n zona de heap sau nu exist un bloc compact de
dimensiune cel puin egal cu size. Dimensiunea size se specific n octei.
Rezultatul funciei malloc fiind un pointer de tip void, se va folosi operatorul cast pentru conversii
de tip la atribuire.
Ex .de alocare dinamic pentru un ir de caractere
S se citeasc de la tastatur un ir de caractere sir1. S se creeze o copie dinamic a acestuia.
#include <stdio.h>
#include <stdlib.h>
void main(void) {
char sir1[40];
char *sir2;
printf("introduceti un sir de caractere: \n");
scanf("%39s", sir1);
if ((sir2=(char *)malloc( (strlen(sir1)+1)*sizeof(char) ))==NULL)
// +1 e pentru \0
{
printf("eroare alocare dinamica memorie !\n");
exit(1);
}
strcpy(sir2, sir1);
printf("copia sirului este: %s \n", sir2);
}

Probleme
Problemele vor fi date la laborator, n mod diferit de la o grup la alta.
Pentru exerciiu:
1. S se impelmenteze funcia strdup, conform specificaiilor (folosii help-ul ca s vedei ce ar
trebui s fac funcia strdup)
2. Citii i afiai o matrice NxM alocat dinamic. Matricea o vom memora ca pe un tablou
obinuit. Accesul la elementul a[i][j] nu se va mai putea face astfel ci a[i*M+j]. Practic
trebuie s calculm al ctelea element din tablou este i s scriem formula corespunztoare.
3. S se impelmenteze funcia strdup, conform specificaiilor (folosii help-ul ca s vedei ce ar
trebui s fac funcia strdup). Parcurgerea irurilor se va face cu variabile de tip pointer.
4. Scriei o funcie care primete 2 iruri ca parametru (sir_sursa si sir_elimin) i creaz un al
treilea ir care va fi de fapt irul sir_sursa din care s-au ters toate apariiile irului
sir_elimin. Noul ir va fi alocat dinamic (astfel nct s ocupe exact atta memorie ct e
necesar) i va fi returnat de ctre funcie.

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