De ce contează?
Gândește-te că citești o rețetă și ajungi la pasul „pregătește sosul". Pui un deget pe acel rând, deschizi rețeta sosului, o faci de la cap la coadă, apoi te întorci exact la degetul lăsat și continui. Un apel de funcție funcționează identic: programul pune un semn unde a rămas, sare în funcție, o execută, apoi revine fix de unde a plecat — cu rezultatul în mână.
Intuiția
Când apelezi o funcție, controlul nu „dispare": el sare în funcție, iar pentru
acel apel se creează un cadru — o cutiuță cu parametrii și variabilele locale ale
funcției. Cât timp funcția lucrează, cadrul ei stă deasupra; când întâlnește return,
cadrul se distruge și controlul revine la apelant, aducând valoarea întoarsă.
Dacă o funcție apelează altă funcție, cadrele se așază unul peste altul ca niște farfurii
pe o stivă: ultimul pus e primul luat.
Vezi cum funcționează
Urmărește stiva de apeluri: fiecare apel adaugă un cadru în vârf, iar fiecare return
îl scoate. Cadrul din vârf e mereu funcția care lucrează chiar acum — stiva crește la
coborâre și se micșorează la urcare.
Folosește ← și → ca să pășești apel cu apel, sau Redă pentru animație.
Urmărește cum fiecare apel adaugă un cadru pe stivă, iar fiecare return îl
scoate — vârful stivei e mereu funcția care lucrează chiar acum.
Vrei să afli media a două numere și apoi media a alte două. Prima idee: pui tot
calculul direct în main, scriind de fiecare dată (a + b) / 2 cu mâna.
Când ai nevoie de aceeași operație în cinci locuri, copiezi același cod de cinci ori.
Dacă găsești o greșeală (de exemplu uiți parantezele și calculezi a + b / 2), trebuie
s-o corectezi în toate cele cinci locuri. Codul devine lung, greu de citit și de depanat.
O funcție e o cutie izolată: o apelezi pe nume, ea primește o copie a argumentelor în propriile variabile, lucrează separat de restul programului și îți întoarce o valoare și controlul. Nu trebuie să știi cum lucrează pe dinăuntru ca s-o folosești — doar ce primește și ce întoarce.
Descompui problema în funcții: scrii o dată funcția media, apoi o apelezi ori de
câte ori ai nevoie. Fiecare apel își creează propriul cadru pe stivă, calculează izolat și
revine cu rezultatul. O corectură într-un singur loc se vede peste tot.
Algoritmul pas cu pas
Urmărim ce face programul când main apelează media(4, 6), iar media apelează la
rândul ei suma(4, 6). Reține regula: vârful stivei este funcția care rulează acum.
Pasul 1 — programul pornește. Pe stivă există doar main:
| cadru | main |
| nivel | 0 |
ruleaza |
Pasul 2 — main apelează media(4, 6). Controlul sare în media; se creează un cadru nou cu a=4, b=6. El se așază deasupra:
| cadru | main | media(4 | 6) |
| nivel | 0 | 1 | |
asteapta | ruleaza |
Pasul 3 — în interiorul lui media, apare apelul suma(4, 6). Încă un cadru se pune deasupra, cu a=4, b=6. Acum trei cadre coexistă:
| cadru | main | media(4 | 6) | suma(4 | 6) |
| nivel | 0 | 1 | 2 | ||
asteapta | asteapta | ruleaza |
Pasul 4 — suma calculează 4 + 6 = 10 și face return 10. Cadrul lui se distruge, controlul revine la media, care primește valoarea 10:
| cadru | main | media(4 | 6) |
| nivel | 0 | 1 | |
primeste-10 |
Pasul 5 — media calculează 10 / 2 = 5 și face return 5. Cadrul ei se distruge, controlul revine la main cu valoarea 5:
| cadru | main |
| nivel | 0 |
primeste-5 |
Observă tiparul: stiva crește la fiecare apel (main → media → suma) și
se micșorează la fiecare return, în ordine inversă (suma → media → main).
Ultimul cadru pus este primul scos. Verificare: suma(4,6)=10, apoi media = 10/2 = 5.
Implementare C++
#include <iostream>
using namespace std;
// aduna doua numere si intoarce suma
int suma(int a, int b) {
return a + b; // un singur cadru, calcul simplu
}
// foloseste suma, apoi imparte la 2
int media(int a, int b) {
int s = suma(a, b); // apel: se creeaza un cadru nou pentru suma
return s / 2; // dupa return-ul lui suma, continuam de aici
}
int main() {
int rezultat = media(4, 6); // main apeleaza media, care apeleaza suma
cout << rezultat << endl; // 5
cout << media(10, 20) << endl; // 15
return 0;
}Fiecare apel își creează propriul cadru. suma nu știe și nu îi pasă cine a chemat-o:
primește două numere, întoarce suma, gata. media o folosește ca pe o cutie neagră. Iar
main le orchestrează pe amândouă. Când controlul revine din media, valoarea 5 ajunge
în rezultat — exact ca degetul care se întoarce pe rândul din rețetă.
Complexitate
Aici nu numărăm operații de algoritm, ci adâncimea stivei — câte cadre coexistă în
momentul cel mai „adânc". Pentru lanțul main → media → suma sunt 3 cadre simultan.
| Caz | Timp | Spațiu |
|---|---|---|
| Un lanț de apeluri de adâncime d | O(d) apeluri | O(d) cadre pe stivă |
Fiecare cadru ocupă memorie (parametrii + variabilele locale). Câtă vreme adâncimea e mică, e neglijabil. Dar dacă o funcție se apelează la nesfârșit (recursie fără oprire), cadrele se tot adună până umplu stiva — atunci programul se oprește cu stack overflow.
Capcane reale la apelul funcțiilor:
- Funcție care nu întoarce pe toate ramurile. Dacă scrii
if (a > 0) return a;și uiți cazulelse, pe acea ramură funcția întoarce o valoare „de gunoi". O funcție cu tip de return trebuie să facăreturnpe orice drum de execuție. - Confunzi definiția cu apelul.
int media(int a, int b) { ... }doar definește funcția — nu o execută. Ca să se întâmple ceva, trebuie s-o apelezi:media(4, 6). Scrierea funcției nu pune niciun cadru pe stivă; doar apelul o face. - Recursie scăpată de sub control. Dacă o funcție se apelează direct sau indirect fără o condiție clară de oprire, stiva crește la nesfârșit și obții stack overflow — nu un rezultat greșit, ci o oprire brutală a programului.