Sunteți pe pagina 1din 54

Ne complicăm puțin, pentru a simplifica rezolvarea unor probleme

Probleme practice:
- O anumită secvență de instrucțiuni se repetă în cadrul unui program;
- Rezolvarea unei anumite sarcini este necesară în mai multe programe, ca de
exemplu:
• diferite operații matematice: extragere radical, ridicare la putere, calcul
funcții trigonometrice;
• operații cu tablouri de memorie;
• diferite operații cu fișiere, etc.
- Descompunerea unei probleme în subprobleme;
- Descompunerea unui algoritm în subalgoritmi.

Toate situațiile de mai sus pot fi rezolvate (elegant) prin utilizarea subprogramelor.

DEFINIȚIE:
Subprogramul este o secvență de instrucțiuni care rezolvă o anumită sarcină și
care poate fi descrisă separat de blocul rădăcină și lansată în execuție din cadrul
unui bloc, ori de câte ori este nevoie.

05/03/2021 1
Terminologie utilizată pentru subprograme
• modul apelant;
• modul apelat

Program apelant(modul Modul


apelant/bloc rădăcină) apelat(subprogram)

Transfer control

Apelare

Revenire control

05/03/2021
În limbajul C++, subprogramele se mai numesc și funcții 2
Avantajele folosirii subprogramelor
În practică, pentru rezolvarea unor probleme complexe trebuie să se conceapă
programe sofisticate numite aplicații. În construirea unei aplicații, folosirea
subprogramelor oferă următoarele avantaje:
• Se face economie de memorie internă. Un grup de instrucțiuni care trebuie să
se execute de mai multe ori într-o aplicație se va scrie o singură dată într-un
subprogram și se va executa prin apelarea subprogramului ori de câte ori este
nevoie;
• Se favorizează lucrul în echipă pentru aplicațiile mari. Fiecare programator va
putea să scrie mai multe subprograme, independent de ceilalți programatori din
echipă(FOARTE IMPORTANT);
• Depanarea și actualizarea aplicației se fac mai ușor. După implementare și
intrarea în exploatare curentă, o aplicație poate necesita modificări, ca urmare a
schimbării unor cerințe. Este mult mai simplu să se gândească modificarea la
nivelul unui subprogram, decât la nivelul întregii aplicații.
• Crește portabilitatea programelor. Subprogramele sunt concepute independent
de restul aplicației și unele dintre ele pot fi preluate, fără un efort prea mare, și în
alte aplicații, în care trebuie să fie rezolvate sarcini similare.

05/03/2021 3
Structura unui subprogram in C++

Un subprogram (funcție) are o definiție și atâtea apeluri câte


sunt necesare.

Definiția unei funcții are forma generală:

tip_returnat nume_functie (lista parametrilor formali)


{
instructiune; // corpul functiei
}

Exemple de funcții-fără corpul funcției:


1. float alfa(int a, int b, float c)
2. void beta(int a, float b, float c, char d)
3. void gama()
05/03/2021 4
Tip_returnat Reprezintă tipul rezultatului calculat și returnat de
funcție și poate fi: int, char, char*, long, float, void,
etc.
În cazul în care tipul rezultatului este diferit de void,
corpul funcției trebuie să conțină cel puțin o
instrucțiune return. Instructiunea return va specifica
valoarea calculată și returnată de funcție care trebuie
să fie de același tip ca si tip_returnat.
Nume_funcție Reprezintă numele dat funcției de către cel ce o
definește, pentru a o putea apela.
Lista_parametrilor_formali Reprezintă o listă de declarații de variabile separate
prin virgulă. Aceasta listă poate să fie și vidă.
Instrucțiune Este o instrucțiune vidă sau o instrucțiune simplă sau
o instrucțiune compusă.

Aici am ramas in 21 decembrie 2016

05/03/2021 5
Altă formă, mai în detaliu

<antetul subprogramului>
{
<declaratii proprii subprogramului>
<instructiuni>
[return <expresie>;]
}
a. Antetul subprogramului. Este o linie de recunoaștere a subprogramului, în
care i se atribuie un nume. El specifică începutul subprogramului.
b. Corpul subprogramului. La fel ca orice bloc C++, este încapsulat într-o
instrucțiune compusă, delimitată de caracterele {...} și este format din două
părți:
• Partea declarativă. Conține definiții de elemente folosite numai în
interiorul subprogramului: tipuri de date, constante și variabile de
memorie. Nu se pot defini și alte subprograme (nu este valabilă tehnica de
imbricare a subprogramelor existentă în alte limbaje de programare).
• Partea executabilă. Conține instrucțiunile prin care sunt descrise acțiunile
realizate de subprogram.

05/03/2021 6
Definirea funcției
Pentru a apela o funcție, aceasta trebuie mai
întâi definită. Astfel, apelul unei funcții trebuie
precedat de definiția funcției respective.
O a doua posibilitate de apelare a funcției constă
în scrierea prototipului funcției înainte ca
aceasta să fie apelată.

05/03/2021 7
Definire funcție
Exemplu
Definire funcție prototip
# include <iostream> # include <iostream>
using namespace std; using namespace std;
// definirea funcției // definire funcție prototip
int max (int a, int b) int max (int, int); // linie terminata cu ;
{ // funcția principala
if (a>b) void main ()
return a; {
else cout << max(10, 20);
return b; }
} // definirea funcției
int max (int a, int b)
// funcția principala {
void main () if (a>b)
{ return a;
cout << max(10, 20); else
} return b;
}

05/03/2021 8
Activarea(apelul) subprogramului

Pentru exemplele precizate în diapozitivul 5 apelul subprogramului se face:


1.
int x,y; float z,w;
w = alfa(x,y,z);
2.
int x; float y,z; char w;
beta(x,y,z,w);
3.
gama();

Parametrii de comunicare

În antetul subprogramului parametrii precizați se numesc parametri formali.


Parametrii formali nu sunt variabile. O variabilă este caracterizată de nume, tip,
și adresă. Legarea unui parametru formal la o adresă se realizează în timpul
execuței instrucțiunii de apelare a subprogramului.

La activarea subprogramului, parametrilor de comunicare li se vor atribui valori


concrete cu care se va executa subprogramul la acel apel. Acești parametri se
numesc parametri actuali.
05/03/2021 9
Transferul parametrilor
Programul principal Subprogramul

x a
void alfa(int x, float y);
y b

alfa(a,b);
x 2
y 3
Parametri formali
alfa(2,3);

regula de
Parametri actuali corespondență

05/03/2021 10
Apel prin valoare și apel prin referință
Exista două tipuri de apel al subprogramelor:
A. Apel prin valoare
B. Apel prin referință

A. Apel prin valoare – se transmite o copie a parametrului actual.


Valorile transmise la apelul unui subprogram sunt memorate în stivă. Datorită
faptului că, după terminarea executiei unui subprogram, stiva este eliberată, în
cazul apelului prin valoare parametrul actual nu se modifică (se operează asupra
unei copii a parametrului efectiv)

B. Apel prin referință - se transmite adresa parametrului actual.


În cazul apelului prin referință, subprogramul, cunoscând adresa parametrului
actual, acționează direct asupra locației de memorie indicată de aceasta,
modificând valoarea parametrului actual.

05/03/2021 11
Utilizarea stivei de către program

În memoria internă, fiecărui subprogram i se alocă o zonă de memorie în


care este încărcat codul executabil. La apelarea unui subprogram,
compilatorul îi predă controlul, adică încep să se execute instrucțiunile
subprogramului, până la întâlnirea unei instrucțiuni return sau până la
sfârșitul blocului care formează corpul subprogramului, după care
compilatorul redă controlul modulului apelant, adică va continua execuția
cu instrucțiunea care urmează, în modulul apelant, imediat după
instrucțiunea care a apelat subprogramul. Acest mecanism de transfer al
controlului se poate realiza deoarece, într-o zonă de memorie internă
numită stiva sistemului (stack), se păstrează temporar informații despre
subprogramul apelant. Aceste informații sunt introduse în stivă atunci
când este apelat subprogramul. Ele formează instanța subprogramului.

05/03/2021 12
Etapele executate la apelarea subprogramului sunt:
1. Se întrerupe execuția modulului apelant.
2. Se pregătește stiva sistemului, astfel:
 se introduce adresa de revenire în modulul apelant;
 se introduc valorile parametrilor cu care a fost apelat subprogramul;
 se rezervă spațiu pentru variabilele locale declarate în subprogram.
3. Se lansează în execuție codul executabil al subprogramului apelat.

Etapele executate la terminarea subprogramului sunt:


1. Se eliberează din stivă spațiul ocupat de variabilele locale și de parametri.
2. Se extrage din stivă adresa de revenire în modulul apelant.
3. Se continuă execuția cu instrucțiunea de la adresa extrasă din stivă.

05/03/2021 13
Astfel, pentru exemplele anterioare de declarații de subprograme, la apelarea lor,
în stivă se vor introduce următoarele informații:
1. float alfa(int a, int b, float c)
2. void beta(int a, float b, float c, char d)
3. void gama()

alfa(x,y,z) beta(x,y,z,w)

variabile locale alfa variabile locale beta

x
x y
y gama()
z
z w variabile locale gama

adresa de adresa de adresa de


revenire revenire revenire

05/03/2021 14
Transferul parametrilor

1. Transfer prin valoare

 Modulul apelant transmite prin parametru, către subprogram, date de


intrare. În momentul apelării subprogramului, o copie a valorii parametrului
este încărcată în stivă. El este văzut în subprogram ca o variabilă locală, care
este inițializată cu valoarea transmisă de modulul apelant prin parametrul
actual din apel. Valoarea acestei variabile se poate modifica în subprogram,
dar această modificare nu se va reflecta și în modulul apelant, deoarece
modificarea se face în stivă, și, la terminarea execuției subprogramului,
zona din stivă în care este memorat parametrul este eliberată.
 Parametrul prin intermediul căruia se face transferul prin valoare se
numește parametru valoare.
 Acest transfer se folosește în general numai pentru parametrii de intrare.
În cazul în care parametrii transmiși prin valoare sunt parametri de ieșire
sau de intrare-ieșire, pentru a putea transmite rezultatul obținut în
subprogram, către modulul apelant, se pot folosi variabile de tip pointeri.

05/03/2021 15
Varianta 1
Transferul parametrilor se face prin valoare.

#include<iostream>
using namespace std;
int schimb(int x, int y)
{int z;
z=x; x=y; y=z;
cout<<"In subprogram\n";
cout<<"a= "<<x<<" b= "<<y;}

int main()
{int a,b;
cout<<"a= "; cin>>a;
cout<<"b= "; cin>>b;
schimb(a,b);
cout<<"\n\nIn programul principal\n";
cout<<"a= "<<a<<" b= "<<b;}
Rezultat după execuție
Valorile lui a și b răman
aceleași in programul
principal
05/03/2021 16
Transferul parametrilor
2. Transfer prin referință

 În momentul apelării subprogramului, în stivă este încarcată adresa de memorie


la care se găsește valoarea parametrului. Subprogramul va lucra direct în zona de
memorie în care se găsește data. Atât modulul apelant cât și subprogramul lucrează
asupra aceleiași date, și orice modificare a valorii acestui parametru făcută în
subprogram se va reflecta și în modulul apelant. La terminarea execuției
subprogramului, este eliberată din stivă zona în care este memorată adresa
parametrului.
 Parametrul prin intermediul căruia se face transferul prin referință se numește
parametru variabilă.
 Acest transfer se recomandă pentru parametrii de intrare-ieșire sau parametrii de
ieșire. Modulul apelant transmite, prin acești parametri, date de intrare-ieșire către
subprogram, subprogramul preia data, o prelucrează și o returnează modulului
apelant. Acest parametru mai poate fi și un rezultat (dată de ieșire) obținut în urma
prelucrărilor din subprogram, care este returnat apoi modulului apelant.
 Distincția dintre un parametru valoare și un parametru variabilă (definirea tipului
de transfer) se face în lista de parametri formali din antetul subprogramului în care
parametrii variabilă sunt precedați de operatorul adresă de memorie *
05/03/2021 17
Varianta 2
Transferul parametrilor se face prin valoare, folosind variabile de tip pointer.

#include<iostream>
using namespace std;
int schimb(int *x, int *y)
{int z;
z=*x; *x=*y; *y=z;
cout<<"In subprogram\n";
cout<<"a= "<<*x<<" b= "<<*y;}

int main()
{int a,b;
cout<<"a= "; cin>>a;
cout<<"b= "; cin>>b;
schimb(&a,&b);
cout<<"\n\nIn programul principal\n";
Rezultat după execuție
cout<<"a= "<<a<<" b= "<<b;}

05/03/2021 18
Varianta 3
Transferul parametrilor se face prin referință.

#include<iostream>
using namespace std;

int schimb(int &x, int &y)


{int z;
z=x; x=y; y=z;
cout<<"In subprogram\n";
cout<<"a= "<<x<<" b= "<<y;}

int main()
{int a,b;
cout<<"a= "; cin>>a;
cout<<"b= "; cin>>b;
schimb(a,b);
cout<<"\n\nIn programul principal\n"; Rezultat după execuție
cout<<"a= "<<a<<" b= "<<b;}

05/03/2021 19
Concluzii

• funcțiile(subprogramele) sunt tot secvențe de programe


în C++
• funcțiile impun regula de transmitere a parametrilor
• parametrii din linia de definire a funcției poartă numele
de parametri formali
• parametrii din linia de apelare a unei funcții poartă
numele de parametri actuali
• pentru funcționarea programelor cu subprograme
(funcții) este utilizat un mecanism de apel cu revenire,
prin intemediul unei structuri de date speciale, stiva
sistemului(stack)
• transmiterea parametrilor intrare-ieșire se face prin
valoare, folosind variabile de tip pointer sau prin referință

05/03/2021 20
Funcții care returnează pointeri

#include<iostream>
using namespace std;

double *f(double *w, int k)


{// w contine adresa de inceput a vectorului a
cout<<"\nIn subprogram\n";
cout<<"w= "<<w<<" *w= "<<*w<<'\n';
return w+=k;
/* incrementeaza pointerul w cu 2-valoarea lui k*/}

05/03/2021 21
int main()
{double a[10]={10,1,2,3,4,5,6,7,8,9};
int i=2;
cout<<"Lungimea unui double= "<<sizeof(double)<<'\n';
cout<<"Adresa lui a este: "<<a<<'\n';
double *pa=a;
cout<<"pa= "<<pa<<'\n'; //pointerul pa contine adresa de
inceput a tabloului a
pa=f(a,i);
cout<<"\nValoarea lui pa este adresa lui a + 16(10)";
cout<<"\npa= "<<pa<<" *pa= "<<*pa<<'\n';
//modific valoarea aflata la adresa data de pa
*pa=1000;
//afisez intregul vector
for(int j=0;j<10;j++)
cout<<a[j]<<'\t';
} // programul frp.cpp
05/03/2021 22
Tablouri ca parametri

#include<iostream>
using namespace std;

int min_tab(int a[], int nr_elem)


{
int elm=a[0];
for(int i=0;i<nr_elem;i++)
if (elm>=a[i])
elm=a[i];
return elm;
}
void citireVector(int b[], int nr_el)
{
for (int i=0;i<nr_el;i++)
{cout<<"Elementul "<<i+1<<" = ";
cin>>b[i];}
}
05/03/2021 23
void afisareVector(int b[], int nr_el)
{
for (int i=0;i<nr_el;i++)
cout<<b[i]<<'\t';
}

int main()
{
int a[10], i,j,n=10;
citireVector(a,n);
afisareVector(a,n);
int min_v=min_tab(a,n);
cout<<"Minimul din vector este: "<<min_v<<endl;
} //programul tabpar.cpp

05/03/2021 24
Pentru tablourile multidimensionale, pentru ca elementele tabloului să poată fi
referite în funcție, compilatorul trebuie informat asupra modului de organizare a
tabloului.

Astfel, pentru tablourile bidimensionale, poate fi omisă doar precizarea


numărului de linii, deoarece pentru a adresa elementul a[i][j], compilatorul
utilizează relația:

&a[i][j] = &a + (i * N + j)*sizeof(tip),

în care -N reprezintă numărul de coloane;


-tip reprezintă tipul tabloului

05/03/2021 25
#include<iostream>
using namespace std;

void citireMatrice(int a[][10], int linii, int coloane)


{
int i,j;
cout<<"Citirea matricii\n";
for (i=0;i<linii;i++)
for(j=0;j<coloane;j++)
{
cout<<"Elem["<<i+1<<","<<j+1<<"]= ";
cin>>a[i][j];
}
}

05/03/2021 26
void afisareMatrice(int a[][10], int linii, int coloane)
{
int i,j;
cout<<"\nAfisarea matricii\n";
for (i=0;i<linii;i++)
{for(j=0;j<coloane;j++)
cout<<a[i][j]<<'\t';
cout<<endl;}

05/03/2021 27
int main()
{
int mat[10][10], i, j, M, N;
do
{
cout<<"Numarul de linii(0<M<=10) : ";
cin>>M;
}
while((M>10)||(M<1));
do
{
cout<<"Numarul de coloane(0<N<=10) : ";
cin>>N;
}
while((N>10)||(N<1));
citireMatrice(mat, M, N);
afisareMatrice(mat, M, N);
} // programul matpar.cpp

05/03/2021 28
Funcții cu parametri impliciți

În limbajul C++ se pot face inițializări ale parametrilor formali. Aceștia se numesc:
parametri impliciți.

tip_returnat nume_functie (lista param neinițializați , par_n=val_n[,….])

Exemplu:
int fct(int x, int y=1);

Sunt permise apelurile:


1. fct(3);
2. fct(3,5);

În cazul 1, parametrul formal x va fi inițializat cu valoarea 3, iar parametrul formal y


cu valoarea precizată, 1.
În cazul 2, parametrul formal x va fi inițializat cu valoarea 3, iar parametrul formal y
cu valoarea 5, fiind ignorată valoarea declarată inițial, 1.

05/03/2021 29
Reguli în ceea ce privește funcțiile cu parametri impliciți

1. Dacă în lista parametrilor formali ai unei funcții există și parametri impliciți și


parametri neinițializați, parametri impliciți trebuie să ocupe ultimele poziții în
listă, nefiind permisă intercalarea acestora.
2. Nu se acceptă definirea funcției ca prototip.
3. La apelare pot lipsi doar parametri de la final spre inceput (vezi programul
param_implic.cpp)

05/03/2021 30
Funcții cu un număr variabil de parametri

În limbajele C și C++ se pot defini funcții cu un număr variabil de parametri.


Parametrii care trebuie să fie prezenți la orice apel al funcției se numesc
parametri fixi, ceilalți se numesc parametri variabili. În antetul unei funcții cu
număr variabil de parametri, parametrii fixi ocupă primele poziții în listă, iar
prezența parametrilor variabili se indică prin trei puncte care se scriu după ultimul
parametru fix al funcției:

<tip_val> nume_functie(<lista parametri fixi>, …);

Vezi programul param_var.cpp

Pentru a-și defini propriile funcții cu număr variabil de parametri, utilizatorul


trebuie să folosească macrouri speciale, care permit accesul la parametrii variabili
și se găsesc în headerul stdarg.h.
va_list lista;
...
va_start(lista, parametru_fix);
va_arg(lista, numar_parametru_variabil);
va_end(lista);
05/03/2021 31
Funcții predefinite

Orice mediu de programare C/C++ este prevăzut cu una sau mai multe biblioteci de
funcții predefinite. Orice bibliotecă este formată din:
-fișierele header (conține prototipurile funcțiilor, declarația de variabile);
- biblioteca (arhiva) propriuzisă (conține definiții de funcții).

Pentru ca funcțiile predefinite să poată fi utilizate într-un program, fișierele header


în care se găsesc prototipurile acestora trebuie incluse printr-o directivă preprocesor,
de exmplu:
#include <stdio.h>
Utilizatorul își poate crea propriile fișiere header, salvate cu extensia “.h”. Acestea vor
fi incluse în programele proprii prin directiva:
#include “headerul_meu.h”

05/03/2021 32
Exemple:

functii pentru alocarea dinamica aflate in <stdlib.h>


functii diverse aflate in <stdlib.h>
functii video pentru modul text aflate in <cono.h>
functii video pentru modul grafic aflate in < graphics.h>
funcii pentru siruri si caractere aflate in <string h> , <ctype.h>
functii de intrareiesire aflate in <stdio.h>
functii matematice aflate in <math.h>
functii de timp si data aflate in <time.h>

05/03/2021 33
Funcții matematice

Principalele functii algebrice si trigonometrice cel mai adesea utlizate in calcule


matematice se gasesc declarate in fisierul antet math.h
Majoritatea functiilor matematice au argumente de tip double sau float si de
asemenea, returneaza o valoare de tip double sau float
FUNCTIA abs
Prototip: int abs (int.x):
Efect: Returneaza valoarea absoluta a numarului intreg x.
FUNCTIA acos.
Prototip: double acos (double x);
Efect: Calculeaza si returneaza valoarea functiei arccos (x). Valoarea returnata va
fi un unghi exprimat in radiouri din intervalul [0, ].
Obs. Daca argumentul x nu este intre -1 si +1 se produce eroarea de domeniu
DOMAIN.
FUNCTIA asin
Prototip: double asin (double x):
Efect: Calculeaza si returneaza valoarea functiei arcsin (x). Valoarea returnata va
fi un unghi in radiani din intervalul [-/2, /2].
Obs.: La fel ca la functia acos.
Vezi programul ecgr2.cpp si functii.cpp
05/03/2021 34
Supraîncărcarea funcțiilor

Supraîncărcarea funcțiilor reprezintă posibilitatea oferită de limbajul C, C++ de a


defini funcții cu același nume, care execută prelucrări diferite.

Supraîncărcarea funcțiilor și a operatorilor(se va vedea în cursurile următoare)


reprezintă acel mecanism ce face posibil polimorfismul în timpul compilării.

Polimorfismul este capacitatea unor entități de a lua forme diferite.

Se pune întrebarea: dacă funcțiile au același nume, atunci cum știe programul să
aleagă funcția corectă. Răspunsul vine sub forma următoarelor reguli:

- funcțiile pot avea același tip, dar atunci trebuie să difere numărul de parametri;
- funcțiile au același număr de parametri, dar trebuie neaparat să fie de alt tip.

05/03/2021 35
Exemplu:

int power(int,int); 
double power(double,double); 
int x=power(2,10); //aici se apeleaza prima varianta 
double y=power(3.14,2.0); //aici se apeleaza a 2-a varianta 

Atentie: Doua declaratii de functii pot parea uneori diferite, fara ca ele sa fie in
realitate distincte, ca in exemplul de mai jos: 

void f(int *p); 


void f(int p[]); 

Cele doua functii declarate mai sus NU sunt diferite intrucat pentru compilator *p
este acelasi lucru cu p[ ]. De ce?

Vezi programul supra1.cpp

05/03/2021 36
Ambiguitati la Supraincarcarea Functiilor 
1. Situatia in care la un apel compilatorul nu poate alege intre doua sau mai multe
functii supraincarcate se numeste ambiguitate. Instructiunile ambigue sunt tratate ca
erori, iar programul nu va fi compilat. Exista in principal doua cai prin care se poate
ajunge la ambiguitate: 

Conversiile automate 

Desi conversiile automate sunt foarte utile ele reprezinta principala cauza a
ambiguitatilor:

#include <iostream> 
using namespace std;
float myFunction(float i) { return i; } 
double myFunction(double i) { return i*i; } 

main() { 
cout << myFunction(10.1) << endl; // OK! 
cout << myFunction(10) << endl; // ambiguu!!! 
} //vezi programul ambiguu1.cpp Aici am ramas in 11 ianuarie 2018

05/03/2021 37
In linia "OK" nu apare nici o ambiguitate intrucat, daca nu sunt specificate
explicit altfel, constantele in virgula mobila in C++ sunt automat de tipul
double. Ambiguitate apare insa atunci cand functia este apelata cu un
parametru de tip intreg (10 de exemplu) intrucat compilatorul nu are de unde sa
stie daca parametrul trebuie convertit in float sau in double. 

Observam asadar ca NU supraincarcarea functiilor in sine genereaza


ambiguitatea, ci apelul functiei cu un argument determinat ! (compilatorul
emite mesaj de eroare cand intalneste apelul in cauza, nu cand intalneste
declaratia functiilor omonime). 

05/03/2021 38
2. Parametrii cu valori implicite

#include <iostream> 
using namespace std;

int myFunction(int i) { return i; } 


int myFunction(int i, int j=1) { return i*j; } 

main() { 
cout << myFunction(10,2) << endl; // OK! 
cout << myFunction(10) << endl; // ambiguu!!! 
}

La primul apel al functiei myFunction() sunt specificate doua argumente, deci


este clar care dintre functii va fi apelata. In cel de-al doilea caz insa apare o
ambiguitate, deoarece compilatorul nu stie daca sa apeleze varianta cu un
parametru a functiei, sau pe cea cu doi parametri, dintre care unul este implicit. 
Vezi programul ambiguu2.cpp

05/03/2021 39
3. Parametrii difera doar prin modul de transmisie (valoare si referinta). 

#include <iostream> 
using namespace std;
int myFunction(int i) { return i; } 
int myFunction(int &i) { return i*10; } 

int main() { 
int a=2; 
cout<<myFunction(5)<<endl; //OK! se apeleaza prima varianta 
cout << myFunction(a) << endl; // ambiguu!!! 
}

La primul apel al functiei myFunction() este specificat ca argument o constanta care


nu poate fi transmisa prin referinta, deci este clar care dintre functii va fi apelata. In
cel de-al doilea caz insa apare o ambiguitate, deoarece compilatorul nu stie daca sa
apeleze varianta cu transmitere prin valoare, sau pe cea cu referinta. Daca varianta
cu referinta ar fi avut semnatura myFunction(const int&), atunci si primul apel al
functiei ar fi fost raportat ca ambiguu.
Vezi programul ambiguu3.cpp

05/03/2021 40
Clase de memorare
Definiții:
Variabilele declarate în afara oricărei funcții sunt variabile globale.
Variabilele declarate în interiorul unui bloc sunt variabile locale.
Porțiunea de cod în care o variabilă este accesibilă reprezintă scopul (domeniul de
vizibilitate) al variabilei respective.

Parametri formali ai unei funcții sunt variabile locale ale funcției respective.
Domeniul de vizibilitate al unei variabile locale este blocul în care variabila respectivă
este definită.
În situația în care numele unei variabile globale coincide cu numele variabilei locale,
varibila locală o “maschează” pe cea globală

Vezi programul variabile.cpp

05/03/2021 41
În cazul variabilelor locale, compilatorul alocă memorie în momentul excuției
blocului sau funcției în care acestea sunt definite. Când execuția funcției sau
blocului se termină, se eliberează memoria pentru aceasta și valorile pentru
variabilele locale se pierd.

Definiții:
Timpul de viață al unei variabile locale este durata de execuție a blocului(sau a
funcției) în care aceasta este definită.
Timpul de viață al unei variabile globale este durata de execuție a programului.

Vezi programul variabile1.cpp

05/03/2021 42
Moduri de alocare a memoriei
Alocarea memoriei(interne) se poate realiza astfel:

- Alocare statică;
- Alocare dinamică;
- Alocare pe stivă.

Alocarea statică se face în următoarele cazuri:


-Pentru instrucțiunile de control propriu-zise;
-Pentru variabilele globale și variabilele declarate în mod explicit static.

Alocarea memoriei pe stivă se face pentru variabilele locale.

Alocarea dinamocă de memorie în mod explicit se face cu ajutorul


funcțiilor de alocare dinamică, care se găsesc în headerul <alloc.h>(în
limbajul C), sau cu ajutorul operatorului new(în limbajul C++).

05/03/2021 43
Așa după cum se cunoaște(sau ar trebui să cunoaștem), pentru toate tipurile de
date(simple sau structurate), la declararea acestora, compilatorul alocă automat
un număr de locații de memorie corespunzător tipului datei.

Vezi programul aloc_stat.cpp

Pentru cazul datelor a căror dimensiune nu se cunoaște apriori, sau variază în


limite largi se recomandă utilizarea alocării dinamice a memoriei. Acest lucru
înseamnă că memoria nu se alocă în momentul compilării ci în momentul
execuției.

05/03/2021 44
Operatorii de alocare dinamică de memorie în C++

În limbajul C++ alocarea dinamică de memorie și elibararea ei se realizează cu


operatorii new și respectiv delete.
Operatorul new este un operator unar, ce oferă posibilitatea alocării dinamice
de memorie atât pentru date de tip predefinit, cât și pentru date de tip definit
de utilizator. Operatorul returnează un pointer la zona de memorie alocată.
Dacă nu există suficientă memorie și alocarea eșuează, operatorul new
returnează pointerul NULL.
Operatorul delete eliberează zona de memorie spre care pointează
argumentul său.

05/03/2021 45
Sintaxa:

<tipdata_pointer> = new <tipdata>; // alocare data simpla


<tipdata_pointer> = new <tipdata>(<val_initializare>); //alocare cu initializare
<tipdata_pointer> = new <tipdata>[nr_elem];//alocare tablou

delete <tipdata_pointer>;//elibereaza zona de memorie alocata dinamic


delete [nr_elem]<tipdata_pointer>;//elibereaza zona de memorie alocata dinamic
tabloului

<tipdata> reprezintă tipul datei (predefinit sau obiect) pentru care se alocă dinamic
memorie, iar < tipdata_pointer> este o variabilă pointer către tipul <tipdata>.

05/03/2021 46
Exemplu 1: se alocă dinamic memorie pentru o dată de tip întreg:
int *pint; // pointer către un un întreg
pint = new int;
//sau
int &i = *new int;

Exemplu 2: se alocă dinamic memorie pentru o dată reală, dublă precizie și se


inițializează cu valoarea15.3
double *p;
p = new double(15.3);
//sau
double &p=*new double(15.3);

Programul aloc_din.cpp, atestat0.cpp

05/03/2021 47
Text preluat de pe adresa: http://alocaredinamica.eu5.org/index.html

Memoria dinamică solicitată de programul nostru este alocată de sistem din


memoria heap.Cu toate acestea,memoria calculatorului este o resursă limitată şi
poate fi epuizată.De aceea este foarte important să se verifice dacă alocarea a fost
reuşită. C++ oferă două metode standard de a verifica dacă alocarea a fost facută
cu succes:
- Una este manipularea excepţiilor.Utilizând această metodă,o excepţie de tipul
bad_alloc este aruncată când alocarea eşuează.Dacă o excepţie este aruncată şi nu
este manipulată de un manipulator specific,execuţia programului este oprită.
-Cealaltă metodă este cunoscută ca nothrow,iar ceea ce se întâmplă atunci cănd o
alocare a memoriei eşuează, este că în loc să se arunce o excepție bad_alloc sau
să se termine programul,pointerul returnat de new este un pointer nul,iar
programul îşi continuă execuţia.Această metodă poate fi specificată utilizînd un
obiect special numit nothrow,declarat în ca atribut al lui new:

Programul atestat0.cpp

05/03/2021 48
CONCLUZIE:
Pentru a putea testa valoarea pointerului, trebuie neaparat sa utilizăm nothrow!!!

Doua afirmatii contradictorii!!!

Variabilele alocate dinamic sunt desființate de către S.O. îndată ce nu mai este nevoie
de ele, dacă nu le ștergem noi cu delete.

Memoria alocatǎ cu new si controlatǎ de pointerul respectiv în schimb nu este


eliberatǎ automat. Respectiva zonǎ de memorie devine indisponibilǎ generând o
gaurǎ de memorie(memory leak). Zona de memorie nu se mai "reface" nici când
programul se terminǎ.

ALTĂ CONCLUZIE: să ștergem cu operatorul delete variabilele alocate dinamic.

SUGESTIE pentru cine vrea sa știe mai mult: de căutat care dintre afirmațiile de mai
sus este adevarată. In mai multe surse credibile de pe Internet. Din documentații.

05/03/2021 49
Funcții recursive
Definiție: o funcție se numește funcție recursivă(sau pe scurt recursivă) dacă ea
se autoapelează fie direct(în definiția ei se face apel la ea însăși), fie indirect (prin
apelul altor funcții)
Limbajele C și C++ dispun de mecanisme speciale care permit suspendarea
execuției unei funcții, salvarea datelor și reactivarea execuției la un alt moment
de timp. Pentru fiecaer apel al funcției, parametrii și variabilele se memorează în
stivă, având valori distincte la fiecare reapelare.

Proiectarea unui algoritm recursiv trebuie să asigure ieșirea din recursivitate. De


aceea apelul recursiv este inclus într-o structură de control de decizie sau
repetitivă.

Exemplul clasic de funcție recursivă este calculul factorialului:


fact(n)=1, dacă n=0 (parte a procesului recursiv care nu se definește prin el
însuși)
fact(n)=n*fact(n-1), dacă n>0

Programul factorial.cpp
05/03/2021 50
05/03/2021 51
Pointeri către funcții
Pointerii către funcții sunt variabile pointer care conțin adresa de început a codului
executabil al unei funcții. Pointerii către funcții permit:
-transferul ca parametru al adresei unei funcții;
- apelul unei funcții cu ajutorul unui pointer către acesta.

Declarația unui pointer către funcție are forma:

<tip> (*<nume_point>)(<lista_declar_param_formali>);
unde:
nume_point este un pointer de tipul “funcție cu rezultatul tipul_valorii_întoarse”. În
declarația precizată mai sus trebuie remarcat rolul parantezelor, penru a face distincție
între declarația unei funcții care întoarce un pointer și declarația unui pointer de funcție:

tip_val_intoarse * nume_point (lista_declar_param_formali);


tip_val_intoarse (* nume_point) (lista_declar_param_formali);

05/03/2021 52
Exemplu:

int f(double , int ); //prototipul funcției f


int (*pf) (double, int); //declararea pointerului pf către funcția f
int i,j;
double d;
pf = f; //atribuie adresa codului executabil al funcțieif pointerului pf
j = *pf(d,i); //apelul funcției f, folosind pointerul către funcție, pf

05/03/2021 53

mulțumesc!

05/03/2021 54