Sunteți pe pagina 1din 42

6/30/2014

1
Cuvinte importante:
- transmiterea parametrilor de tip tablou unidimensional;
- transmiterea parametrilor de tip tablou bidimensional;
- transmiterea parametrilor de tip structura catre o functie;
- variabile globale si variabile locale;
- clase de memorare: auto, static, register, extern;
- recursivitate, functii recursive, utilizarea recursivitatii;
- crearea functiilor cu un numar variabil de parametrii cu tipuri
multiple;
- parametrii functiei main ().
6/30/2014
2
Transmiterea parametrilor de tip tablou unidimensional
La definirea/declararea unei functii, se poate utiliza pentru declararea parametrului
formal de tip tablou una din constructiile echivalente:
<tip> * <nume_variabila> sau
<tip> <nume_variabila> [] sau
<tip> <nume_variabila> [<max>]
unde:
- <tip> - specifica tipul elementelor tabloului unidimensional;
- <nume_variabila> - specifica numele parametrului formal de tip tablou
unidimensional;
- <max> - specifica numarul maxim de elemente din tabloul unidimensional.
Nota: Aceste constructii sunt echivalente deoarece numele unui tablou este un
pointer constant catre primul element al tabloului.
La apelul unei functii, care are parametrii de tip tablou unidimensional, trebuie sa se
transmita doar numele tabloului, deoarece este el insusi o adresa. Compilatorul
plaseaza in stiva adresa de inceput a tabloului si nu elementele tabloului.
6/30/2014
3
Transmiterea parametrilor de tip tablou bidimensional
La definirea/declararea unei functii, se poate utiliza pentru declararea parametrului
formal de tip tablou una din constructiile echivalente:
<tip> * <nume_variabila> [] sau
<tip> <nume_variabila> [] [<max2>] sau
<tip> <nume_variabila> [<max1>] [<max2>]
unde:
- <tip> - specifica tipul elementelor tabloului bidimensional;
- <nume_variabila> - specifica numele parametrului formal de tip tablou
bidimensional;
- <max1> - specifica numarul maxim de linii din tabloul bidimensional;
- <max2> - specifica numarul maxim de coloane din tabloul bidimensional.
La apelul unei functii, care are parametrii de tip tablou bidimensional, trebuie sa se
transmita doar numele tabloului, deoarece este el insusi o adresa.
6/30/2014
4
Urmatorul program (functii_tablouri.cpp) ilustreaza modul de definire si de apel pentru
doua functii, ce utilizeaza parametrii-tablouri, care efectueaza urmatoarele:
- genereaza toate numerele prime mai mici decat n;
- calculeaza produsul elementelor de deasupra diagonalei unei matrice patratice cu n linii
si n coloane.
Parametrul care reprezinta matricea (matr) este transmis in functia prod_sus_dig () sub
forma de tablou de pointeri:
Nota:
Generarea tuturor numerelor prime mai mici decat n s-a realizat folosind metoda ciurul
lui Eratostene.
Metoda consta in a pune in ciur toate numerele mai mici decat n, apoi de a cerne
aceste numere pana raman in ciur numai numerele prime. Astfel, mai intai cernem
(eliminam din ciur) toti multipli lui 2, apoi cernem multipli lui 3 s.a.m.d.
Ciurul este reprezentat ca un vector cu n componente care pot fi 0 sau 1, cu semnificatia:
ciur[i] = 1, daca numarul i este in ciur (este prim) si
ciur[i] = 0 daca numarul i nu este in ciur (nu este prim).
Vectorul ciur se parcurge de la 2 (cel mai mic numar prim) pana la . n
6/30/2014
5
#include <iostream.h>
void gen_nr_prim (int *, int &);
double prod_sus_dig (int *[], int &);
void main (void)
{int *a, nr;
int **matr; // adresa de inceput a vectorului de pointeri la linii
int lin, col, aloc = 0;
int inum = 1;
int i, j;
double produs;
cout << "Introduceti numarul n pana la care se vor genera numere prime: ";
cin >> nr;
a = new int [nr];
if (a)
{
gen_nr_prim (a, nr);
delete a;
}
else
cout << "Heap-ul este plin";
6/30/2014
6
cout << "Introduceti numarul de linii ale matricei patratice "; cin >> lin;
col = lin;
matr = new int *[lin]; // alocare zona de memorie pentru pointerii de linie
if (!matr)
{
cout << "Heap-ul este plin\n";
aloc = 1;
}
else
{
for (i = 0; i < lin; i++)
{
matr[i] = new int[col]; // alocare zona de memorie pentru coloanele fiecariei linii
if (!matr[i])
{
cout << "Heap-ul este plin\n";
aloc = 1;
}
else
aloc = 0;
}
}
6/30/2014
7
if (!aloc)
{
for (i=0; i<lin; i++)
for (j=0;j<col; j++)
matr[i][j]=inum++;
produs = prod_sus_dig (matr, lin);
cout << "Produsul elementelor de deasupra diagonalei principale este " << produs;
for (i=0; i < lin; i++)
delete[] matr [i]; //dealocarea zonei elementelor (coloanelor) matricei
delete[] matr; //dealocarea zonei pointerilor de linie
}
}
void gen_nr_prim (int ciur [], int &n)
{int i, j;
for (i=2; i<n; i++) //initial toate numerele sunt in ciur, presupuse prime
ciur [i] = 1;
for (i=2; i*i<=n; i++)
if (ciur[i]) //i este prim
for (j=2; j*i<n; j++) // se cern toti multiplii lui i
ciur[i*j] = 0;
for (i=2; i<n; i++)
if (ciur[i])
cout << "Numarul prim generat este " << i << endl;}
6/30/2014
8
double prod_sus_dig (int *a[], int &n)
{int i, j;
double p = 1;
for (i=0; i<n-1; i++)
for (j=i+1; j<n; j++)
p = p*a[i][j];
return p;}
6/30/2014
9
Urmatorul program (functii_tablouri1.cpp) este o varianata a primului program, dar
parametrul care reprezinta matricea (matr) este transmis in functie prod_sus_dig () sub
forma de tablou bidimensional:
#include <iostream.h>
void gen_nr_prim (int *, int &);
double prod_sus_dig (int [][10], int &);
void main (void)
{int *a, nr;
int matr [10][10];
int lin, col;
int inum = 1;
int i, j;
double produs;
cout << "Introduceti numarul n pana la care se vor genera numere prime: "; cin >> nr;
a = new int [nr];
6/30/2014
10
if (a)
{
gen_nr_prim (a, nr);
delete a;
}
else
cout << "Heap-ul este plin";
cout << "Introduceti numarul de linii ale matricei patratice "; cin >> lin;
col = lin;
for (i=0; i<lin; i++)
for (j=0;j<col; j++)
matr[i][j]=inum++;
produs = prod_sus_dig (matr, lin);
cout << "Produsul elementelor de deasupra diagonalei principale este " << produs;}
6/30/2014
11
void gen_nr_prim (int ciur [], int &n)
{int i, j;
for (i=2; i<n; i++) //initial toate numerele sunt in ciur, presupuse prime
ciur [i] = 1;
for (i=2; i*i<=n; i++)
if (ciur[i]) //i este prim
for (j=2; j*i<n; j++) // se cern toti multiplii lui i
ciur[i*j] = 0;
for (i=2; i<n; i++)
if (ciur[i])
cout << "Numarul prim generat este " << i << endl;}
double prod_sus_dig (int a[][10], int &n)
{int i, j;
double p = 1;
for (i=0; i<n-1; i++)
for (j=i+1; j<n; j++)
p = p*a[i][j]; return p;}
6/30/2014
12
Transmiterea parametrilor de tip structura catre o functie
La fel ca orice variabila, compilatorul de C/C++ permite transmiterea unei structuri
catre o functie.
Pentru a modifica membrii structurii in cadrul unei functii, trebuie sa se transmita
catre functie un pointer la structura. In cadrul functiei, pentru dereferentierea
membrilor pointerului la structura se folosesc doua formate:
a) Formatul cu operatorul de dereferentiere asterisc (*), astfel:
(*pointer).membru = valoare; unde: pointer este un pointer de tip structura.
Compilatorul de C/C++ incepe cu parantezele, obtinand mai intai locatia structurii.
Apoi, el adauga la adresa deplasamentul membrului specificat.
Nota: Daca se omit parantezele, compilatorul de C/C++ presupune ca insusi
membrul este un pointer si utilizeaza operatorul asterisc (*) pentru dereferentierea
acestui pointer-membru sub forma:
*pointer.membru_pointer = valoare;
unde: membru_pointer este un membru de tip pointer al structurii pointer.
b) Formatul ce foloseste operatorul ->, astfel:
pointer -> membru = valoare;
6/30/2014
13
Urmatorul program (functii_structuri.cpp), apeleaza functia modific_structura() care
modifica valorile continute intr-o structura de tip Schita:
#include <stdio.h>
struct Schita
{
int tip;
int culoare;
float raza;
float aria;
float perimetru;
};
void modific_structura(struct Schita *schita)
{
(*schita).tip = 0;
(*schita).culoare = 1;
(*schita).raza = 5.0;
(*schita).aria = 22.0 / 7.0 * (*schita).raza * (*schita).raza;
(*schita).perimetru = 2.0 * 22.0 / 7.0 * (*schita).raza;
}
6/30/2014
14
void main(void)
{
struct Schita cerc;
modific_structura(&cerc);
printf("cerc.tip %d\n", cerc.tip);
printf("cerc.culoare %d\n", cerc.culoare);
printf("cerc.raza %f\ncerc.aria %f\ncerc.perimetru %f\n",
cerc.raza, cerc.aria, cerc.perimetru);
}
Nota:
1. Se observa ca tipul structura Schita este creata in afara functiei main () deoarece
ea trebuie sa fie utilizabila atat de functia main () cat si de functia
modific_structura().
2. Pentru a modifica membrii structurii in cadrul unei functii trebuie sa se transmita
structura prin adresa (la fel ca si la transmiterea unei variabile obisnuite care se
doreste a fi modificata). In cazul de fata, parametrul de tip structura transmis
functiei modific_structura() este de forma &cerc.
6/30/2014
15
Variabile globale si variabile locale
Variabilele globale au urmatoarele caracteristici:
- sunt declarate in exteriorul tuturor functiilor programului;
- sunt stocate in zona de date a programului, memoria alocata fiind automat
initializata cu 0; memoria ramane alocata, pentru variabilele globale, pana la sfarsitul
executiei programului;
- sunt utilizabile (vizibile) din momentul declararii in toate functiile definite ale
programului (fisierului-sursa), chiar si in alte fisiere-sursa care compun un proiect.
Variabilele locale au urmatoarele caracteristici:
- sunt declarate in corpul (blocul) unei functii;
- sunt stocate in stiva, memoria alocata nefiind initializata automat; memoria ramane
alocata pana la sfarsitul executiei functiei sau blocului de instructiuni in care au fost
declarate variabilele locale;
- sunt utilizabile (vizibile) din momentul declararii numai in blocul in care sunt
declarate.
6/30/2014
16
Urmatorul program (variabile_globale.cpp) ilustreaza folosirea a trei variabile
globale, numite a, b si c:
#include <stdio.h>
int a = 1, b = 2, c = 3; // variabile globale
float calcul_globale(void)
{
float z; // variabila locala
a = 9;
z = (float)(a + b + c)/5;
printf("a = %d b = %d c = %d z = %.2f\n", a, b, c, z);
return z;
}
void main(void)
{
printf("a = %d b = %d c = %d z = %.2f\n", a, b, c, calcul_globale());
}
Dupa executia programului pe ecran se afiseaza:
a = 9 b = 2 c = 3 z = 2.80
6/30/2014
17
Evitarea utilizarii variabilelor globale
La o prima privire, folosirea variabilelor globale pare a simplifica programarea,
deoarece elimina necesitatea folosirii parametrilor in functii si, mai important,
necesitatea intelegerii apelului prin valoare si a apelului prin referinta.
Din pacate, in loc sa reduca numarul de erori, variabilele globale adesea il sporesc.
Deoarece codul-sursa poate modifica valoarea unei variabile globale in oricare loc
din program, este foarte dificil pentru un alt programator care citeste programul sa
gaseasca fiecare loc din program in care variabila globala se modifica. De aceea, alti
programatori ar putea sa modifice programul fara sa inteleaga pe deplin efectul pe
care modificarea il are asupra variabilei globale.
Ca regula, functiile trebuie sa modifice doar acele variabile care le sunt transmise ca
parametri. In acest fel, programatorii pot sa studieze prototipurile functiilor pentru a
determina rapid care variabila este modificata de o functie.
Daca programul foloseste variabile globale, ar fi bine sa se reconsidere conceptia
programului. Obiectivul unui programator trebuie sa fie eliminarea (sau cel putin
minimizarea) folosirii variabilalor globale.
6/30/2014
18
Nota:
1. In interiorul functiilor (blocurilor de instructiuni) in care sunt declarate, variabilele
locale au prioritate fata de variabilele globale cu acelasi nume. Prin urmare, in cazul
in care in interiorul unei functii se declara o variabila locala cu acelasi nume ca al
unei variabile globale, numai variabila locala este utilizabila si vizibila pe parcursul
executiei functiei si nu variabila globala cu acelasi nume.
2. Daca numele unui parametru formal al unei functii coincide cu al unei variabile
globale, pe durata executiei functiei respective, parametrul formal are prioritate fata
de variabila globala cu acelasi nume.
Limbajul C++ permite accesarea si utilizarea, in cadrul aceluiasi bloc de
instructiunii, atat a variabilei locale cat si a variabilei globale cu acelasi nume.
Pentru a face utilizabila si vizibila si variabila globala ce are acelasi nume cu
variabila locala se foloseste operatorul unar denumit operator de rezolutie globala
C++.
Sintaxa folosita pentru utilizarea operatorului de rezolutie este:
::<nume_variabila_globala>
6/30/2014
19
Urmatorul program (operator_rezolutie.cpp) ilustreaza modul de utilizare a
operatorului de rezolutie globala C++:
#include <iostream.h>
int y = 1001; //variabila globala
void main(void)
{
int y = 1; //variabila locala

cout << "Valoarea variabilei locale " << y << '\n';
cout << "Valoarea variabilei globale " << ::y << '\n';
}
Dupa executia programului, pe ecran se afiseaza urmatoarele:
Valoarea variabilei locale 1
Valoarea variabilei globale 1001
6/30/2014
20
Clase de memorare
Clasa de memorare precizeaza zona de memorie in care este alocata variabila - in
zona de date a programului, in stiva etc. Exista patru clase de memorare care pot fi
utilizate impreuna cu tipurile de date:
- clasa auto;
- clasa static;
- clasa register;
- clasa extern.
Clasa de memorare a unei variabile (locale sau globale) se poate specifica in mod
explicit precizand inaintea declaratiei variabilei un cuvant-cheie care reprezinta
specificatorul de clasa de memorare.
Clasa de memorare auto si specificatorul auto se poate utiliza numai pentru
variabile locale si indica faptul ca variabila este alocata pe stiva, ceea ce se intampla
in mod implicit. De aceea, specificatorul auto este optional si se foloseste destul de
rar.
De exemplu, instructiunea
auto int i, j; este echivalenta cu int i, j;
6/30/2014
21
Variabilele auto au valori initiale nedefinite (contin valori reziduale) si domeniul lor
de existenta este blocul de instructiuni care le contine declaratia. Inainte de utilizare
variabilele auto trebuie initializate. Variabilele auto, din interiorul functiilor, nu retin
valorile anterioare la urmatorul apel al functiei.
Clasa de memorare static si specificatorul static indica faptul ca variabila nu este
memorata pe stiva, ci in zona de date a programului.
Clasa de memorare static ofera o cale de a retine valoarea unei variabile in timpul
executiei programului.
In cazul unei variabile locale, specificatorul static indica faptul ca variabila este
initializata automat cu 0 si are zona de memorie alocata de la prima executie a
blocului de instructiuni, in care a fost declarata, pana la sfarsitul programului
(aceasta insemnand ca isi pastreaza valoarea intre doua apeluri consecutive ale
functiei).
Domeniul de utilizare (vizibilitate) nu se modifica, adica variabila ramane vizibila
numai in interiorul blocului in care a fost declarata.
6/30/2014
22
Avantajele utilizarii variabilelor locale statice sunt:
- variabilele sunt alocate o singura data, nu la fiecare apel al functiei;
- isi pastreaza valorile intre doua apeluri consecutive ale functiei;
- domeniul de vizibilitate fiind local, nu exista riscul ca valoarea variabilei sa fie
alterata de alte functii ale programului, ca in cazul variabilelor globale;
- este o solutie de alocare a unor variabile locale, daca spatiul disponibil pe stiva
devine o problema.
In cazul unei variabile globale, specificatorul static indica faptul ca variabila poate fi
utilizata numai in cadrul fisierului-sursa in care a fost declarata (adica nu poate fi
utilizata in alte fisiere-sursa care compun proiectul in ansamblul lui). Cu alte cuvinte,
variabila devine locala (privata) fisierului-sursa in care a fost declarata.
Specificatorul static poate fi folosit si pentru definirea functiilor intr-un fisier-sursa.
In acest caz, functia are domeniu de utilizare (apelare) numai in fisierul-sursa
respectiv, nefiind recunoscuta in alte fisiere-sursa ale proiectului.
6/30/2014
23
Urmatorul program (variabile_statice.cpp) ilustreaza utilizarea unei variabile locale
statice in cadrul unei functii. Functia numara() contine variabila locala statica y.
La primul apel al functiei numara(), variabila y este initializata cu valoarea 100, apoi
valoarea variabilei este afisata si incrementata cu 1.
La al doilea si al treilea apel al functiei numara(), variabila y, fiind statica, nu mai este
nici alocata, nici initializata, ci isi pastreaza valoarea de la apelul precedent (adica
valoarea 101). Aceasta valoare va fi afisata si apoi incrementata. Daca nu s-ar fi
declarat variabila y utilizand specificatorul static, functia numara () ar fi afisat
intotdeauna valoarea 100 si ar fi actualizat valoarea variabilei y numai la 101.
#include <iostream.h>
void numara(void)
{ static int y = 100;
cout << "Se afiseaza numarul " << y << endl;
y++ ;}
void main (void)
{ numara ();
numara ();
numara ();}
6/30/2014
24
Clasa de memorare register si specificatorul register indica faptul ca variabila
poate fi memorata in registrii interni ai calculatorului. Prin urmare, daca este posibil
(adica exista registrii disponibili), variabila va fi memorata in unul dintre registrii
microprocesorului, nu in memoria RAM. Numai variabilele locale pot avea aceasta
clsa de memorare, iar regulile de utilizare raman cele de la variabilele de clasa auto.
Operatorul de adresa (de referentiere) nu se poate aplica unei variabile register.
Utilizarea specificatorului register poate fi utila, de exemplu, pentru o variabila
contor intr-o instructiune repetitiva, sau pentru operatii cu pointeri.
Utilizarea variabilelor register este dependenta de calculator si compilator. Este
posibil ca toate declaratiile register sa fie ignorate de compilator sau sa se lucreze cu
ele fara a fi cerut explicit acest lucru (ca in cazul Borland C++).
Clasa de memorare extern si specificatorul extern permite unui fisier-sursa
utilizarea variabilelor si functiilor din alte fisiere-sursa. Toate declaratiile de variabile
globale si de functii care nu contin cuvantul-cheie static sunt accesibile din alte
fisiere-sursa. Prin urmare, variabilele globale si functiile sunt implicit externe.
Fisierul-sursa care utilizeaza variabile globale si functii din alt fisier-sursa trebuie sa
contina declaratia acestora precedata de cuvantul-cheie extern.
Variabilele externe se folosesc pentru a comunica date intre fisiere-sursa care
alcatuiesc un proiect.
6/30/2014
25
Recursivitate. Functii recursive
In cursul executiei unui program, o functie poate apela o alta functie, care apeleaza la
randul ei alta, care si ea, poate apela multe alte functii. Intr-o astfel de inlantuire,
fiecare functie executa o anumita operatie. Limbajul C/C++ permite chiar ca o
functie sa se apeleze pe ea insasi.
O functie recursiva este o functie care se apeleaza pe sine insasi pentru a efectua o
anumita operatie.
Procesul prin care o functie se apeleaza pe sine poarta numele de recursivitate.
La scrierea unei functii recursive trebuie avute in vedere doua aspecte:
- conditia de terminare (incheiere);
- numarul de apeluri recursive.
Sa analizam aceste doua aspecte pe cateva exemple.
Calculul factorialului constituie un exemplu simplu de recursivitate.
Relatia: n! = n(n-1)! permite o rezolvare recursiva, deoarece se poate calcula
factorialul unui numar n, daca se cunoaste factorialul numarului n-1.
Conditia de terminare este 1! = 1.
6/30/2014
26
Nota: Numarul de apeluri recursive este limitat doar de dimensiunea memoriei disponibile,
deoarece mecanismul recursivitatii plaseaza mereu in stiva rezultatele partiale.
Urmatorul program (fact_recursiv.cpp), creaza o functie recursiva, factorial() si apoi
foloseste functia pentru a returna factorialul unui numar introdus de la tastatura:
#include <stdio.h>
int factorial(int n)
{int rezultat;
printf("Sunt in functia factorial cu n = %d\n", n);
if (n == 1)
{ printf("Returneaza valoarea 1\n");
return(1);}
else
{rezultat = n * factorial(n-1);
printf ("Returneaza %d * factorial (%d) = %d\n", n, n-1, rezultat);
return(rezultat);}}

void main(void)
{int nr, fact;
printf("Introduceti un numar: ");
scanf("%d", &nr);
fact=factorial(nr);
printf("Factorialul lui %d este %d\n", nr, fact);}
6/30/2014
27
Dupa executia programului, pe ecran se afiseaza:
Introduceti un numar: 5
Sunt in functia factorial cu n = 5
Sunt in functia factorial cu n = 4
Sunt in functia factorial cu n = 3
Sunt in functia factorial cu n = 2
Sunt in functia factorial cu n = 1 // ultimul apel al functiei recursive
Returneaza valoarea 1
Returneaza 2 * factorial (1) = 2
Returneaza 3 * factorial (2) = 6
Returneaza 4 * factorial (3) = 24
Returneaza 5 * factorial (4) = 120
Factorialul lui 5 este 120
Se evidentiaza nivelul de imbricare in apelul functiei recursive factorial ().
6/30/2014
28
Un alt exemplu (sir_invers.cpp) este inversarea unui sir de de caractere. Dandu-se un sir de
caractere citit de la tastatura, functia recursiva creez_invers () va crea un nou sir care este
inversul sirului citit si apoi va afisa pe ecran sirul de caractere creat in ordine inversa.
#include <iostream.h>
void creez_invers(char *sir, char *sir1)
{static int i =0;
cout << "Sunt in functia creez_invers cu "<< sir << endl;
if (*sir)
{
creez_invers(sir+1, sir1);
sir1 [i] = *sir;
i++;
sir1 [i] = '\0';
cout << "Sfarsit apel recursiv cu " << sir1 << endl;
}}
void main(void)
{char sir_sursa[64];
char sir_destinatie[64];
cout << "Introduceti sirul de inversat:\n";
cin.getline (sir_sursa, sizeof(sir_sursa));
creez_invers(sir_sursa, sir_destinatie);
cout << sir_destinatie;}
6/30/2014
29
Dupa executia programului, pe ecran se afiseaza:
Introduceti sirul de inversat:
adriana
Sunt in functia creez_invers cu adriana
Sunt in functia creez_invers cu driana
Sunt in functia creez_invers cu riana
Sunt in functia creez_invers cu iana
Sunt in functia creez_invers cu ana
Sunt in functia creez_invers cu na
Sunt in functia creez_invers cu a
Sunt in functia creez_invers cu // ultimul apel al functiei recursive
Sfarsit apel recursiv cu a
Sfarsit apel recursiv cu an
Sfarsit apel recursiv cu ana
Sfarsit apel recursiv cu anai
Sfarsit apel recursiv cu anair
Sfarsit apel recursiv cu anaird
Sfarsit apel recursiv cu anairda
anairda
6/30/2014
30
Programul urmator (hanoi_recursiv.cpp) ilustreaza modul de rezolvare prin recursivitate
a problemei turnurilor din Hanoi.
Problema turnurilor din Hanoi este un joc vechi de origine asiatica, in care un anumit
numar de discuri este plasat pe trei tije A, B, C. Discurile au diametre diferite si sunt
gaurite la mijloc, astfel incat sa poata intra pe cele trei tije.
Initial, toate discurile sunt situate pe tija A.
Obiectivul jocului este de a transfera toate discurile de pe tija A pe tija C.
Restrictiile jocului sunt: a) la un moment dat, se poate deplasa numai un singur disc; b)
nici un disc nu poate fi asezat deasupra unui disc mai mic decat el.
Algoritmul recursiv de rezolvare a problemei turnurilor din Hanoi.
Presupunem ca dorim sa deplasam toate discurile de pe un turn sursa (notat cu S) pe unul
destinatie (notat cu D). Exista un turn intermediar (notat cu I).
Presupunem ca avem n discuri pe turnul S, asezate de jos in sus in ordinea marimii
discurilor (discul cel mai mare - n este la baza si discul cel mai mic - 1 este in varf).
Algoritmul de rezolvare este:
1. Primele n-1 discuri se deplaseaza de pe S pe I;
2. Discul ramas (cel mai mare) se deplaseaza de pe S pe D.
3. Primele n-1 discuri se deplaseaza de pe I pe D.
6/30/2014
31
#include <iostream.h>
void hanoi (int, char, char, char);
void main()
{int nr;
cout << "Introduceti numarul de discuri: " ;
cin >> nr;
hanoi (nr, 'A', 'B', 'C');}

void hanoi (int n, char sursa, char inter, char desti)
{
cout << "In functia hanoi cu n = " << n << " " << sursa << inter << desti << endl;
if (n==1)
cout << "Discul 1 de pe " << sursa << " pe " << desti << endl;
else
{
hanoi (n-1, sursa, desti, inter); //muta discurile de pe sursa pe turnul intermediar
cout << "Sfarsit apel recursiv hanoi-1 pentru n = " << n << endl;
cout << "Discul " << n << " de pe " << sursa << " pe " << desti << endl; //muta discul
de la baza
hanoi (n-1, inter, sursa, desti); //muta discurile de pe turnul intermediar la destinatie
cout << "Sfarsit apel recursiv hanoi-2 pentru n = " << n << " " << endl;
}
}
6/30/2014
32
Dupa executia programului pe ecran se afiseaza:
Introduceti numarul de discuri: 3
In functia hanoi cu n = 3 ABC
In functia hanoi cu n = 2 ACB
In functia hanoi cu n = 1 ABC
Discul 1 de pe A pe C
Sfarsit apel recursiv hanoi-1 pentru n = 2
Discul 2 de pe A pe B
In functia hanoi cu n = 1 CAB
Discul 1 de pe C pe B
Sfarsit apel recursiv hanoi-2 pentru n = 2
Sfarsit apel recursiv hanoi-1 pentru n = 3
Discul 3 de pe A pe C
In functia hanoi cu n = 2 BAC
In functia hanoi cu n = 1 BCA
Discul 1 de pe B pe A
Sfarsit apel recursiv hanoi-1 pentru n = 2
Discul 2 de pe B pe C
In functia hanoi cu n = 1 ABC
Discul 1 de pe A pe C
Sfarsit apel recursiv hanoi-2 pentru n = 2
Sfarsit apel recursiv hanoi-2 pentru n = 3
6/30/2014
33
Utilizarea recursivitatii
Avantaje
Recursivitatea este foarte utilizata la obiecte ce se preteaza la o definire recursiva,
cum ar fi listele si arborii. Folosirea recursivitatii face ca programul rezultat sa fie
mai compact si deseori mai usor de conceput si de inteles decat codul echivalent
nerecursiv.
In practica exista anumite tipuri de probleme a caror solutie nu este determinabila pe
baza unor calcule cunoscute, ci pe baza unor metode de incercari, cunoscute sub
denumirea de nedeterminism si revenire. In astfel de probleme, recursivitatea este
metoda cea mai convenabila. Solutia unei astfel de probleme se obtine din solutii
partiale, rezultate din apeluri recursive, care se modifica dinamic.
Avantajul folosirii recursivitatii rezida in posibilitatea revenirii la locurile de decizie
prin insusi mecanismul recursivitatii.
6/30/2014
34
Dezavantaje
Totusi, functiile recursive sunt adesea considerabil mai lente decat corespondentele
lor nerecursive.
Functiile recursive sunt lente pentru ca la fiecare apel se introduce in program o
suprasarcina de apel a functiei (overhead).
Suprasarcina de apel reprezinta intervalul de timp necesar calculatorului pentru a
incarca si a descarca informatiile din stiva. Desi, calculatorul poate executa rapid
aceste operatii de incarcare si descarcare, totusi ele consuma timp.
Apelul functiei recursive de un numar prea mare de ori va determina cresterea
considerabila a duratei de executie a programului.
De asemenea, utilizarea apelului recursiv necesita atentie la necesarul de stiva. Daca
numarul de apeluri recursive ale functiei este prea mare, s-ar putea depasi
capacitatea stivei (stack overflow).
6/30/2014
35
Crearea functiilor cu un numar variabil de parametrii cu tipuri
multiple
Pentru a crea functii care accepta un numar variabil de parametrii se pot utiliza
macroinstructiunile va_start, va_arg si va_end, definite in fisierul antet stdarg.h.
Cum functioneaza macroinstructiunea va_start ?
Se stie ca, atunci cand programul apeleaza o functie, compilatorul plaseaza
parametrii in stiva de la dreapta la stanga. In cadrul unei functii cu un numar variabil
de parametrii, macroinstructiunea va_start initializeaza lista de parametrii ai functiei
la primul parametru fixat din lista variabila (atribuie unui pointer adresa primului
parametru fixat din lista variabila).
Macroinstructiunea este definita in fisierul antet stdarg.h astfel:
void va_start(va_list ap, lastfix);
unde:
- ap - specifica pointerul care memoreaza adresa primului parametru fixat al functiei;
acest pointer este declarat cu tipul va_list specificat in fisierul antet stdarg.h;
- lastfix - specifica primul parametru fixat din lista variabila de parametrii ai functiei.
6/30/2014
36
Cum functioneaza macroinstructiunea va_arg ?
Macroinstructiunea va_arg returneaza valoarea indicata de pointerul fiecarui
parametru din lista variabila a functiei. Pentru a determina valoarea,
macroinstructiunea trebuie sa cunoasca tipul de data a parametrului. Dupa regasirea
valorii, macroinstructiunea va incrementa pointerul, astfel incat sa indice urmatorul
parametru din stiva. Pentru a determina numarul de octeti adaugati pointerului,
macroinstructiunea va folosi din nou tipul de data al parametrului.
Macroinstructiunea este definita in fisierul antet stdarg.h astfel:
tip va_arg(va_list ap, tip)
unde:
- tip - specifica tipul de data al parametrului functiei;
Cum functioneaza macroinstructiunea va_end ?
Dupa ce macroinstructiunea va_arg gaseste ultimul parametru al functiei,
macroinstructiunea va_end sterge lista de parametrii din stiva.
Macroinstructiunea este definita in fisierul antet stdarg.h astfel:
void va_end(va_list ap);
6/30/2014
37
Observatii:
1. In antetul functiei care are un numar variabil de parametrii se trec puncte de suspensie
() pentru a indica un numar variabil de parametrii.
De exemplu:
double ad_valori(char *sir, ...)
2. In corpul functiei care are un numar variabil de parametrii se declara o variabila-pointer
de tip va_list (tip specificat in fisierul antet sdtarg.h). Acesta variabila-pointer este
folosita la apelul macroinstructiunilor va_start, va_arg si va_end.
Urmatorul program (functie_param_variabili.cpp) foloseste un numar variabil de
parametrii (care accepta valori de toate tipurile) pentru a aduna toate valorile transmise
functiei ad_valori() de catre functia apelanta. Functia returneaza o valoare de tip double.
Pentru a se putea determina tipul parametrilor, se transmite functiei un specificator de
format similar cu cel din printf.
De exemplu, pentru a aduna valori intregi si in virgula mobila, se foloseste urmatorul
model de apel:
sum = ad_valori("%f %d %f %d", 1.1, 1, 2.2, 3);
Antetul functiei ad_valori() este de forma:
double ad_valori(char *sir, ...)
6/30/2014
38
#include <stdio.h>
#include <stdarg.h>
double ad_valori(char *sir, ...)
{va_list marcator;
double suma = 0.0;
va_start(marcator, sir); // marcheaza primul parametru transmis
while (*sir) // examineaza fiecare caracter din sir
{
if (*sir == '%') // daca nu este un specificator de format,%_, sarim peste
{
switch (*(++sir))
{
case 'd': suma += va_arg(marcator, int);
break; // marcheaza urmatorul parametru transmis
case 'f': suma += va_arg(marcator, double);
break; // marcheaza urmatorul parametru transmis
}
}
sir++;
}
va_end(marcator); // anuleaza valoarea pointerului parametrului
return(suma);}
6/30/2014
39
void main(void)
{double sum;
sum = ad_valori("%f", 3.3);
printf("Rezultatul este %f\n", sum );
sum = ad_valori("%f %f", 1.1, 2.2);
printf("Rezultatul este %f\n",sum );
sum = ad_valori("%f %d %f", 1.1, 1, 2.2);
printf("Rezultatul este %f\n", sum);
sum = ad_valori("%f %d %f %d", 1.1, 1, 2.2, 3);
printf("Rezultatul este %f\n", sum);}
Parametrii functiei main ()
Functia main () este apelata automat la lansarea in executie a programului. Functia
main () accepta si ea parametrii. Parametrii functiei main () sunt transmisi la
lansarea in executie a programului din sistemul de operare in mod linie de comanda.
Argumentele din linia de comanda sunt succesiuni de caractere separate prin spatii
sau caractere tab. Ele pot varia functie de necesitatile programului.
Formatul general al unei linii de comanda in sistemul de operare DOS este:
c:\> nume_program argument1 argument2
6/30/2014
40
Pentru functia main () se pot utiliza trei parametrii:
main (int argc, char * argv[], char * env[]);
unde:
- argc - specifica numarul de argumente din linia de comanda (argc >0);
- argv - este un vector de pointeri de tip caracter la cele argc argumente din linia de
comanda (argv[0] este numele programului);
- env - este un vector de pointeri de tip caracter la informatii de mediu ale sistemului
de operare (de exemplu, prompterul sistemului, cai implicite de cautare), ultimul
element al vectorului fiind NULL, pentru a marca sfarsitul listei.
De exemplu, programul urmator (arg_main.cpp) permite trei optiuni in linia de
comanda si anume /l sau /t sau /s. Pentru linia de comanda:
>arg_main /l
parametrul argc are valoarea 2, iar pointerii argv[0] si argv[1] refera sirurile
arg_main si respectiv /l.
6/30/2014
41
#include <stdio.h>
main ( int argc, char *argv[])
{
if (argc!=2)
{
printf ("Utilizare program: %s /l sau /t sau /s\n", argv[0]);
return 1;
}
if (argv [1][0] == '/')
switch (argv[1][1])
{
case 'l' : printf("optiunea /l"); break;
case 't' : printf("optiunea /t"); break;
case 's' : printf("optiunea /s"); break;
default : printf ("Optiune ilegala pentru %s\n", argv[0]);
return 1;
}
else
{
printf("Optiune nespecificata pentru %s\n", argv[0]);
return 1;
}
}
6/30/2014
42
Executia programului pentru diferite linii de comanda afiseaza:
C:\mariana\Limbaj C\programe>arg_main
Utilizare program: C:\mariana\Limbaj C\programe\arg_main.exe /l sau /t sau /s
C:\mariana\Limbaj C\programe>arg_main /l
optiunea /l
C:\mariana\Limbaj C\programe>arg_main /t
optiunea /t
C:\mariana\Limbaj C\programe>arg_main /s
optiunea /s
C:\mariana\Limbaj C\programe>arg_main /
Optiune ilegala pentru C:\mariana\Limbaj C\programe\arg_main.exe
C:\mariana\Limbaj C\programe>arg_main l
Optiune nespecificata pentru C:\mariana\Limbaj C\programe\arg_main.exe

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