Sunteți pe pagina 1din 8

1

Pointeri

- Variabile i adrese

n limbajul C, orice variabil are o adres: o valoare numeric, ce indic locul
din memorie unde e memorat valoarea variabilei.
Operatorul prefix & d adresa operandului: &x reprezint adresa variabilei
x. O adres poate fi tiprit (n hexazecimal) cu formatul %p n printf

#include <stdio.h>
double d; int a[10]; /* variabile globale */
int main(void)
{
int k; /* variabila locala */
printf("Adresa lui d: %p\n", &d); /* de ex. 0x80496c0 */
printf("Adresa lui a[0]: %p\n", &a[0]); /* 0x80496e0 */
printf("Adresa lui a[5]: %p\n", &a[5]); /* 0x80496f4 */
printf("Adresa lui k: %p\n", &k); /* 0xbffff8e4 */
} /* Obs &a[5] - &a[0] == 5 * sizeof(int) (pozitii consecutive) */

- Tipuri pointer. Declarare. Indirectare

Orice expresie n C are un tip, la fel i expresiile adres. Dac variabila x are
tipul tip, &x are tipul tip *
int x; // &x are tipul int *, adica pointer la int (adres de int)
char c; // &c are tipul char *, (pointer la char, adres de char)
Exist tipuri de adres diferite pentru fiecare tip de date. Putem declara variabile de
aceste tipuri (pointeri):
tip * nume_var; // nume_var e pointer la (adresa pt.) o valoare de tipul tip
pointer = o variabil care conine adresa altei variabile
Operatorul prefix * d obiectul *p de la adresa dat de operandul p.
Operandul = pointer. Rezultatul = referin la obiectul indicat de pointer sau operator
de indirectare (derefereniere, referire indirect prin adres)
Dac pointerul p are tipul tip *, atunci *p are tipul tip. Sintaxa declaraiei
(aceeai dar citit n dou feluri) sugereaz folosirea:
char* p; // p e o variabila de tipul char * (adres de char)
char *p; // *p (obiectul de la adresa p) are tipul char

- Operatorii de adres i derefereniere

Pointerii au adrese, ca orice variabile:
int *p; // adresa &p are tipul int **
int ** pp = &p; // pp are tipul int **, adic adresa unei adrese de int
dar putem citi int* *pp sau int **pp, deci *pp are tipul int * (adres de int)
i **pp are tipul int (val. de la adr. *pp)

2

nainte de folosire, un pointer trebuie iniializat, de ex. cu adresa unei variabile
de tipul potrivit:
int x, *p, **pp; p = &x; pp = &p;
O referin *p poate fi folosit la stnga sau la dreapta unei atribuiri (n cazul
de mai sus, *p se folosete absolut la fel (sinonim) cu x)
int x, y, z, *p;
p = &x;
z = *p; /* z = x */ *p = y; /* x = y */
Operatorii adres & i de indirectare * sunt unul inversul celuilalt:
*&x este chiar x, pentru orice obiect (variabil) x
&*p are valoarea p, pentru orice variabil pointer p (dar p e o variabil i poate fi
atribuit; &*p e o expresie i nu poate)

- Pointeri ca argumente/rezultate de funcii

n limbajele de programare exist dou mecanisme principale de transfer ale
parametrilor: transferul prin valoare i transferul prin referin.
n C parametrii se transfer numai prin valoare - aceasta nseamn c
parametrii actuali sunt copiai n zona de memorie rezervat pentru parametrii formali.
Modificarea parametrilor formali se va reflecta asupra copiilor parametrilor actuali i
nu afecteaz parametrii actuali, adic parametrii actuali nu pot fi modificai !
Astfel funcia:
void schimb(int a, int b)
{ int c=a;
a=b;
b=c;
}
nu produce interschimbul parametrilor actuali x i y din funcia main():
void main(void)
{ int x=10,y=15;
printf(%d\t%d\n, x, y);
schimb(x, y);
printf(%d\t%d\n, x, y);
}
Se afieaz:
10 15
10 15
Forma corect a funciei schimb() obinut prin simularea transferului prin
referin cu pointeri este:
n C n C++
void schimb(int *a, int *b)
{ int c;
c = *a;
*a = *b;
*b = c;
}
void schimb(int &a, int &b)
{ int c;
c = a;
a = b;
b = c;
}
- Alocarea dinamic a memoriei
3
Utilizatorul poate solicita n timpul execuiei programului alocarea unei zone de
memorie. Aceast zon de memorie poate fi eliberat, n momentul n care nu mai
este necesar. Alocarea i eliberarea de memorie la execuie permite gestionarea
optim a memoriei. Biblioteca standard ofer 4 funcii, avnd prototipurile n
<alloc.h> i <stdlib.h>. Acestea sunt:

void *malloc(unsigned n);
Funcia aloc un bloc de memorie de n octei. Funcia ntoarce un pointer la nceputul
zonei alocate. n caz c cererea de alocare nu poate fi satisfcut, funcia returneaz
NULL.
void *calloc(unsigned nelem, unsigned dim);
Aloc nelem*dim octei de memorie (nelem blocuri formate
din dim octei fiecare). ntoarce un pointer la nceputul zonei
alocate sau NULL. Memoria alocat este iniializat cu zerouri.

void free(void *p);
Funcia elibereaz o zon de memorie indicat de p, alocat n prealabil prin
malloc() sau calloc().
void *realloc(void *p, unsigned dim);
Modific dimensiunea spaiului alocat prin pointerul p, la dim. Funcia
ntoarce adresa zonei de memorie realocate, iar pointerul p va adresa zona realocat.
- dac dimensiunea blocului realocat este mai mic dect a blocului iniial, p nu se
modific, iar funcia va ntoarce valoarea lui p.
- dac dim==0 zona adresat de p va fi eliberat i funcia ntoarce NULL.
- dac p==NULL, funcia aloc o zon de dim octei (echivalent cu malloc()).
Funciile de alocare ntorc pointeri generici (void*) la zone de memorie, n
timp ce utilizatorul aloc memorie ce pstreaz informaii de un anumit tip. Pentru a
putea accesa memoria alocat, indirect, prin intermediul pointerului, acesta va trebui
s fie un pointer cu tip, ceea ce impune conversia explicit (prin cast) a pointerului
ntors de funcia de alocare ntr-un pointer cu tip.
De exemplu, pentru a aloca un vector de ntregi, avnd n elemente vom folosi:
int *p:
if (p=(int*)malloc(n*sizeof(int))==NULL) {
printf(Memorie insuficienta\n);
exit(1);
}
Funciile care ntorc pointeri sunt utile n alocarea de spaiu pentru variabile
dinamice. Variabilele dinamice sunt alocate n momentul execuiei, nu au nume i
sunt accesate prin pointeri. Un constructor este o funcie care aloc spaiu n mod
dinamic pentru o variabil i ntoarce un pointer la spaiul rezervat. Un destructor este
o funcie care primete un pointer la o zon alocat dinamic i elibereaz aceast zon.
O funcie util, strdup() salveaz un ir de caractere ntr-o zon alocat
dinamic i ntoarce un pointer la acea zon sau NULL.
char *strdup(char *s)
{ char *p;
p=(char*) malloc(strlen(s)+1);
4
if (p!=NULL)
strcpy(p,s);
return p;
}

n C++, alocarea dinamic se face mai simplu i mai sigur, folosind operatorii
new i delete.
Operatorul new permite alocarea de memorie n heap. El se folosete ntr-una
din formele:
tip *p, *q, *r;
p=new tip; //rezerva in heap o zona de sizeof(tip) octeti
q=new tip(expresie);//acelasi efect cu initializare cu
val.expresiei
r=new tip[expint]; //rezerva o zona neinitializata de
// expint*sizeof(tip) octeti
Eliberarea memoriei alocate se face prin:
delete p;
delete [] r; //elibereaza memoria alocata pentru tablou

Metoda Greedy

Metoda Greedy (greedy=lacom) este aplicabil problemelor de optim.
Considerm mulimea finit A={a
1
,...,a
n
} i o proprietate p definit pe
mulimea submulimilor lui A (notat P(A)):
p:P(A){0,1} cu

c
= |
X Y p(Y), p(X)
1 ) p(

O submulime S c A se numete soluie dac p(S)=1.
Dintre soluii dorim s alegem una care optimizeaz o funcie f:P(A)R dat.
Metoda urmrete evitarea parcurgerii tuturor submulimilor (ceea ce ar
necesita un timp de calcul exponenial), mergndu-se "direct" spre soluia optim.
Nu este ns garantat obinerea unei soluii optime; de aceea aplicarea metodei
Greedy trebuie nsoit neaprat de o demonstraie.
Distingem dou variante generale de aplicare a metodei Greedy:
S|
for i=1,n
xalege(A);
AA\{x}
if p(S{x})=1
SS{x}
prel(A)
S|
for i=1,n
if p(S{a
i
})=1
SS{a
i
}
Observaii:
5
- n algoritm nu apare funcia f
- timpul de calcul este liniar (exceptnd prelucrrile efectuate de procedurile
prel i alege);
- dificultatea const n a concepe procedurile alege, respectiv prel, n care
este "ascuns" funcia f.

Exemplul 1 Se consider mulimea de valori reale A={a
1
,...,a
n
}. S se determine
o submulime a lui A a crei sum a elementelor este maxim.
Soluie. Vom parcurge mulimea i vom selecta numai elementele pozitive. Dac nu
exist elemente pozitive, soluia este dat de cel mai mare element din mulime.
Corectitudinea soluiei este evident.

Exemplul 2 (Contraexemplu) Se consider mulimea A={a
1
,...,a
n
} cu elemente
pozitive. S se determine o submulime a lui A de sum maxim, dar cel mult egal
cu o valoare M dat.
Dac alegem la fiecare pas cel mai mare element care poate fi adugat la
soluie fr a depi M, pentru A={6,3,4,2} i M=7 obinem {6}. Dar soluia optim
este {3,4} cu suma egal cu 7.
Continum cu prezentarea unor exemple clasice.

- Memorarea textelor pe band

n texte cu lungimile L(1),...,L(n) urmeaz a fi aezate pe o band. Pentru
a citi textul de pe poziia k, trebuie citite textele de pe poziiile 1,2,...,k (conform
specificului accesului secvenial pe band). S se determine o modalitate de aezare a
textelor pe band astfel nct timpul mediu de acces s fie minimizat.

Soluie. O soluie nseamn o permutare peS
n
.
Pentru o astfel de permutare (ordine de aezare a textelor pe band), timpul
pentru a citi textul de pe poziie k este: T
p
(k)=L(p
1
)+...+L(p
k
). Presupunnd
textele egal probabile, trebuie minimizat valoarea =
=
n
1 k
p
(k) T
n
1
T(p) .
S observm c funcia T se mai poate scrie:
+ =
=
n
1 k
k
) 1)L(p k (n
n
1
T(p)

Conform strategiei Greedy, ncepem prin a ordona cresctor vectorul L.
Rezult c n continuare L(i)sL(j), i<j.

6
Propoziie. Dac L(1) s L(2) s s L(n) atunci poziionarea corespunztoare
permutrii identice este optim.

Demonstraie.
Fie
1
( , , )
n
p p p = o poziionare optim ( ( ) ( )
n
S T p T o o e s ). Presupunem prin
reducere la absurd c p nu este permutarea identic. Rezult ca exist i, j, 1 i j n s < s
astfel nct ( ) ( )
i j
L p L p > . Fie p' permutarea obinut din p prin interschimbarea
poziiilor i i j:
1 1 1 1 1
( , , , , , , , , , )
i j i j i j n
p p p p p p p p p
+ +
' = . Avem:
1
1
( ) ( 1) ( )
n
k
k
T p n k L p
n
=
= +


( ) ( ) ( 1) ( ) ( 1) ( ) ( 1) ( ) ( 1) ( )
i j j i
T p T p n i L p n i L p n j L p n j L p ' = + + + + + + =
( ) ( ) ( ) ( 1) ( ) ( ) ( )( ( ) ( )) ( )
i j i j
T p i j L p j L p T p i j L p L p T p = + + = + <
Am obinut ( ) ( ) T p T p ' < . Contradicie!

Generalizare. Memorarea a n texte pe m benzi.
Strategia Greedy: La fiecare pas se alege textul cel mai scurt dintre cele rmase i se
plaseaz pe banda cea mai libera. Textele se memoreaz n ordinea cresctoare a
lungimilor lor. Fiecare text se plaseaz pe banda urmtoare celei pe care a fost plasat
textul anterior.
Demonstraia corectitudinii: tem.
- Problema continu a rucsacului

Se consider un rucsac de capacitate (greutate) maxim G i n obiecte
caracterizate prin:
- greutile lor g
1
,...,g
n
;
- ctigurile c
1
,...,c
n
obinute la ncrcarea lor n totalitate n rucsac.
Din fiecare obiect poate fi ncrcat orice fraciune a sa.
S se determine o modalitate de ncrcare de (fraciuni de) obiecte n rucsac,
astfel nct ctigul total s fie maxim.
Soluie. Prin soluie nelegem un vector x=(x
1
,,x
n
) cu

s
e
=
G x g
i [0,1], x
n
1 i
i i
i

O soluie optim este soluie care maximizeaz funcia =
=
n
1 i
i i
x c f(x) .

Dac suma greutilor obiectelor este mai mic dect G, atunci ncrcm toate
obiectele: x=(1,...,1). De aceea presupunem n continuare c g
1
+...+g
n
>G.
Conform strategiei Greedy, ordonm obiectele descresctor dup ctigul la
unitatea de greutate, deci lucrm n situaia:
7
n
n
2
2
1
1
g
c
...
g
c
g
c
> > > (*)

Algoritmul const n ncrcarea n aceast ordine a obiectelor, atta timp ct nu
se depete greutatea G (ultimul obiect poate fi eventual ncrcat parial):

G1 G { G1 reprezint greutatea disponibil }
for i=1,n
if g
i
sG1
x
i
1; G1G1-g
i

else
x
i
G1/g
i
;
for j=i+1,n
x
j
0
stop
write(x)

Am obinut deci x=(1,...,1,x
j
,0,...,0) cu x
j
e[0,1).

Corectitudine. Artm c soluia astfel obinut este optim.

Fie y soluia optim care are cele mai multe elemente n comun cu x:
y=(...,y
k
,...) cu

=
=
=
n
1 i
i i
n
1 i
i i
y c
G y g
maxim

Dac y=x, fie k prima poziie pe care y
k
=x
k
. Atunci avem
ksj: pentru k>j se depete G.
y
k
<x
k
:
pentru k<j: evident, deoarece x
k
=1;
pentru k=j: dac y
k
>x
k
se depete G.

Considerm soluia: y=(y
1
,...,y
k-1
,x
k
,oy
k+1
,..., oy
n
) cu o<1
(primele k-1 componente coincid cu cele din x). Pstrm greutatea total G, deci:
g
k
x
k
+o(g
k+1
y
k+1
+...+g
n
y
n
)=g
k
y
k
+g
k+1
y
k+1
+...+g
n
y
n
.
Rezult:
g
k
(x
k
-y
k
)=(1-o)(g
k+1
y
k+1
++g
n
y
n
) (**)

Comparm performana lui y' cu cea a lui y:
8
f(y)-f(y) = c
k
x
k
+oc
k+1
y
k+1
+...+ oc
n
y
n
- (c
k
y
k
+c
k+1
y
k+1
+...+c
n
y
n
) =
= c
k
(x
k
-y
k
) + (o-1)(c
k+1
y
k+1
+...+c
n
y
n
) =
= c
k
/g
k
[g
k
(x
k
-y
k
)+(o-1)(g
k
/c
k
c
k+1
y
k+1
+...+g
k
/c
k
c
n
y
n
)]
Dar o-1<0 i g
k
/c
k
s g
s
/c
s
, s>k.
Atunci f(y)-f(y)>c
k
/g
k
[g
k
(x
k
-y
k
)+(o-1)(g
k+1
y
k+1
+...+g
n
y
n
)]=0,
deci f(y') > f(y). Deoarece y este optim, rezult c f(y') = f(y). Contradicie
(y' are mai multe elemente n comun cu x).

Problema discret a rucsacului difer de cea continu prin faptul c fiecare
obiect poate fi ncrcat numai n ntregime n rucsac.
S observm c aplicarea metodei Greedy eueaz n acest caz. ntr-adevr,
aplicarea ei pentru: G=5, n=3 i g=(4,3,2), c=(6,4,2.5)
are ca rezultat ncrcarea primul obiect; ctigul obinut este 6. Dar ncrcarea
ultimelor dou obiecte conduce la ctigul superior 6.5.

Exerciii
1. Problema spectacolelor: Se dau n spectacole prin ora de nceput i ora de
sfrit. S se selecteze dintre aceste spectacole un numr maxim de spectacole
care nu se suprapun.
2. Se dau mulimile de numere ntregi nenule
1 2
{ , , , }
m
A a a a = i
1 2
{ , , , }
n
B b b b = ,
unde m n. S se determine o submulime
1 2
{ , , , }
m
B x x x ' = a lui B astfel nct
suma
1 1 2 2 m m
a x a x a x + + + s fie maxim i s se precizeze cum s-a obinut.
3. Se dau n compozitori i pentru fiecare dintre ei anii de via. S se tipreasc
numrul maxim de compozitori contemporani.
4. Interclasarea optim a n iruri ordonate. Se dau lungimile a n iruri ordonate,
1 2
, , ,
n
L L L . Dorim s obinem un ir ordonat cresctor care conine toate
elementele celor n iruri iniiale, interclasnd succesiv perechi de iruri. tiind c
interclasarea a dou iruri de lungimi A, respectiv B, necesit A+B deplasri, s se
determine o ordine n care trebuie s se realizeze interclasrile astfel nct
numrul total de deplasri s fie minim.