Invatare Atomica

Vreau sa inteleg exact cum ruleaza programul meu!

Progres lectie:
0%
🎯

Obiectivul lectiei

Cand ai mai multe functii, programul "sare" de la una la alta. Cum stii exact ce se intampla si in ce ordine?

Dupa aceasta lectie vei putea:

  • Sa explici cum functioneaza apelarea unei functii
  • Sa explici exemplu simplu: main cheama o functie
  • Sa aplici ce este call stack-ul
  • Sa creezi construim comportament complex din piese simple
  • Sa aplici o functie poate sa se apeleze pe ea insasi

Incearca singur!

🎯 INCEARCA

Urmareste executia!

Copiaza codul, ruleaza-l si observa ordinea in care se afiseaza mesajele. Apoi modifica-l.

Misiunea ta (5 minute):
1
Copiaza codul de mai jos si lipeste-l pe OneCompiler. Apasa Run.
#include <iostream> using namespace std; void salutare() { cout << "[salutare] Incep!" << endl; cout << "[salutare] Buna ziua!" << endl; cout << "[salutare] Termin!" << endl; } void despartire() { cout << "[despartire] Incep!" << endl; cout << "[despartire] La revedere!" << endl; cout << "[despartire] Termin!" << endl; } void conversatie() { cout << "[conversatie] Incep!" << endl; salutare(); cout << "[conversatie] Suntem la mijloc." << endl; despartire(); cout << "[conversatie] Termin!" << endl; } int main() { cout << "[main] Incep programul!" << endl; conversatie(); cout << "[main] Gata!" << endl; return 0; }
▶ Deschide OneCompiler
2
Numara cate linii se afiseaza in total. Scrie pe hartie ordinea mesajelor inainte de a rula. Ai ghicit corect?
3
Adauga o noua functie void introducere() { cout << "Ma numesc Robot!" << endl; } si cheam-o din conversatie() intre salutare() si despartire(). Ce se schimba?
4
Incearca sa stergi functia salutare() dar sa o lasi apelata in conversatie(). Ce eroare primesti?
🌟 BONUS: Creeaza o functie int dublu(int x) care returneaza x*2 si o functie int triplu(int x) care returneaza x*3. Apoi afiseaza dublu(triplu(2)). Ce rezultat obtii?
Nu stii ordinea la pasul 2? Click aici

Urmareste regulile simple:

1. Programul incepe INTOTDEAUNA din main()

2. Cand intalneste un apel de functie, sare la acea functie

3. Cand functia se termina, se intoarce exact de unde a plecat

Deci: main → conversatie → salutare → (inapoi in conversatie) → despartire → (inapoi in conversatie) → (inapoi in main)

Blocat la BONUS? Click aici

Compozitia functioneaza din interior spre exterior:

dublu(triplu(2)) → mai intai se calculeaza triplu(2) = 6

Apoi se calculeaza dublu(6) = 12

Rezultat: 12

1

Cum functioneaza apelarea unei functii?

Cum functioneaza apelarea unei functii?

Cand programul intalneste un apel de functie (de ex. salutare();), se intampla 3 lucruri:

1. Programul pune un semn unde a ramas (isi noteaza pozitia curenta)

2. Sare la codul functiei apelate si il executa linie cu linie

3. Cand functia se termina, se intoarce exact la locul marcat si continua de acolo

📖 Analogie: Cartea cu semne de carte

Citesti o carte (main). La pagina 50 scrie "vezi Anexa B". Pui un semn de carte la pagina 50, mergi la Anexa B, o citesti, apoi te intorci la pagina 50 si continui. Daca in Anexa B scrie "vezi Nota 3", pui un ALT semn de carte in Anexa B, mergi la Nota 3, o citesti, te intorci in Anexa B, termini, si apoi te intorci la pagina 50.

2

Exemplu simplu: main cheama o functie

Exemplu simplu: main cheama o functie

Sa urmarim exact ce se intampla cand rulam acest program:

Cod
void salut() { cout << "Salut!" << endl; // Linia B } int main() { cout << "Inainte" << endl; // Linia 1 salut(); // Linia 2 - APEL! cout << "Dupa" << endl; // Linia 3 }
👁 Pas cu pas:
Pas Ce se executa Output Unde suntem
1 Linia 1: cout << "Inainte" Inainte in main()
2 Linia 2: salut(); → SALT! - main() → salut()
3 Linia B: cout << "Salut!" Salut! in salut()
4 salut() se termina → INAPOI - salut() → main()
5 Linia 3: cout << "Dupa" Dupa in main()

Output final: Inainte apoi Salut! apoi Dupa

3

Ce este Call Stack-ul?

Ce este Call Stack-ul?

Calculatorul tine o stiva (stack) cu functiile active. Cand apelezi o functie, o pune deasupra stivei. Cand functia se termina, o scoate de pe stiva. Functia din varful stivei este cea care ruleaza acum.

Gandeste-te la o stiva de farfurii: pui o farfurie deasupra (apel) si o scoti de deasupra (return). Ultima pusa = prima scoasa.

4

Construim comportament complex din piese simple

Construim comportament complex din piese simple

Puterea functiilor sta in faptul ca le poti combina. O functie poate chema oricata alta functie. Asta iti permite sa construiesti programe complexe din bucati mici, usor de inteles.

Exemplu: Robot care face micul dejun
void fierbeApa() { cout << "Fierb apa..." << endl; } void puneCafea() { cout << "Pun cafeaua in cana..." << endl; } void facCafea() { fierbeApa(); // cheama alta functie puneCafea(); // cheama alta functie cout << "Cafeaua e gata!" << endl; } void facToast() { cout << "Prajesc painea..." << endl; cout << "Toast gata!" << endl; } void micDejun() { cout << "=== Incep micul dejun ===" << endl; facCafea(); // facCafea cheama fierbeApa + puneCafea facToast(); // functie separata cout << "=== Mic dejun servit! ===" << endl; } int main() { micDejun(); }
👁 Ordinea de executie:
1 main() cheama micDejun()
2 afiseaza "=== Incep micul dejun ==="
3 micDejun() cheama facCafea()
4 facCafea() cheama fierbeApa() → "Fierb apa..."
5 facCafea() cheama puneCafea() → "Pun cafeaua..."
6 "Cafeaua e gata!" → facCafea() se termina
7 micDejun() cheama facToast() → "Prajesc..." + "Toast gata!"
8 "=== Mic dejun servit! ===" → totul gata!
5

O functie poate sa se apeleze pe ea insasi!

O functie poate sa se apeleze pe ea insasi!

Aceasta tehnica se numeste recursivitate. Functia se cheama pe sine, dar de fiecare data cu o valoare diferita. Trebuie sa aiba o conditie de oprire, altfel ruleaza la infinit!

Exemplu: Numaratoare inversa
void numaraInvers(int n) { if (n <= 0) { // Conditia de OPRIRE! cout << "START!" << endl; return; // Nu mai apela din nou } cout << n << "..." << endl; numaraInvers(n - 1); // Se cheama pe sine cu n-1 } int main() { numaraInvers(3); } // Output: // 3... // 2... // 1... // START!
Call Stack la recursivitate: numaraInvers(3)

Apel 1

numaraInvers(3)
main()

Apel 2

numaraInvers(2)
numaraInvers(3)
main()

Apel 3

numaraInvers(1)
numaraInvers(2)
numaraInvers(3)
main()

Apel 4 (STOP)

numaraInvers(0)
numaraInvers(1)
numaraInvers(2)
numaraInvers(3)
main()

La n=0, conditia de oprire se activeaza. Stiva incepe sa se goleasca: fiecare functie se intoarce la cea de dedesubt.

⚠ Fara conditie de oprire = Dezastru!

Daca uiti conditia de oprire, functia se cheama pe sine la infinit. Stiva creste si creste pana cand... CRASH! Eroarea se numeste "stack overflow" (stiva a dat pe-afara). Programul se opreste fortat.

6

Rezultatul unei functii ca argument pentru alta

Rezultatul unei functii ca argument pentru alta

Poti folosi valoarea returnata de o functie direct ca argument pentru alta functie. Se evalueaza de la interior spre exterior (ca in matematica).

Exemplu: Compozitie de functii
int dublu(int x) { return x * 2; } int triplu(int x) { return x * 3; } int main() { cout << dublu(triplu(2)); // ??? }
Cum se evalueaza dublu(triplu(2))?
triplu(2)
6
dublu(6)
12

Pasul 1: Se evalueaza triplu(2) = 2 * 3 = 6
Pasul 2: Rezultatul (6) devine argument: dublu(6) = 6 * 2 = 12

Mai multe exemple de compozitie
// Exemplul 1: Trei functii inlantuite cout << dublu(dublu(triplu(1))); // triplu(1) = 3 → dublu(3) = 6 → dublu(6) = 12 // Exemplul 2: Rezultat stocat in variabila int rezultat = dublu(triplu(5)); // 5*3=15, 15*2=30 cout << rezultat; // 30 // Exemplul 3: Folosind rezultatul in expresii int a = dublu(3) + triplu(2); // 6 + 6 = 12
7

Fiecare functie are propriile variabile

Fiecare functie are propriile variabile

Variabilele declarate in interiorul unei functii sunt locale - ele exista DOAR in acea functie. Cand functia se termina, variabilele dispar. Alte functii nu le pot accesa.

Gresit - nu merge!
void calcul() { int x = 10; // x exista DOAR in calcul() } int main() { calcul(); cout << x; // EROARE! x nu exista aici! }
Corect - cu return
int calcul() { int x = 10; return x; // "trimite" valoarea lui x inapoi } int main() { int rezultat = calcul(); // primeste valoarea 10 cout << rezultat; // 10 - merge! }
8

Regula: Defineste inainte de a apela

Regula: Defineste inainte de a apela

In C++, compilatorul citeste codul de sus in jos. Daca apelezi o functie care nu a fost inca definita, primesti eroare. Solutia: scrie functia deasupra lui main, sau foloseste un prototip.

Gresit - functia nu e definita inca
int main() { salut(); // EROARE! Compilatorul nu stie ce e salut() } void salut() { cout << "Buna!"; }
Solutia 1: Defineste INAINTE de main
void salut() { // Definitia vine PRIMA cout << "Buna!"; } int main() { salut(); // Acum merge! }
Solutia 2: Foloseste un PROTOTIP
void salut(); // Prototip: "promisiune" ca functia exista int main() { salut(); // OK! Compilatorul stie ca exista salut() } void salut() { // Definitia completa vine mai tarziu cout << "Buna!"; }
💡 Cand folosim prototipuri?

Prototipurile sunt utile cand ai mai multe functii care se cheama reciproc, sau cand vrei ca main() sa fie primul lucru pe care il citesti in fisier. In programe mici, e suficient sa definesti functiile deasupra lui main.

Exercitii practice

Exercitiul 1 (Nivel minim) - Traseaza executia

Analizeaza urmatorul program si raspunde la intrebari:

void gamma() { cout << "G "; }
void beta() { cout << "B1 "; gamma(); cout << "B2 "; }
void alpha() { cout << "A1 "; beta(); cout << "A2 "; beta(); cout << "A3 "; }
int main() { alpha(); }
  1. Scrie pe hartie, pas cu pas, ordinea exacta in care se afiseaza mesajele. Care este output-ul complet?
  2. De cate ori apare "G " in output? De ce?
  3. Deseneaza call stack-ul in momentul in care se executa gamma() pentru prima data. Cate "etaje" are stiva?

Raspunde numerotat: 1. ... 2. ... 3. ...

Exercitiul 2 (Nivel standard) - Compozitie de functii

Cerinta: Scrie 3 functii: int patrat(int x) care returneaza x*x, int succ(int x) care returneaza x+1, si int dublu(int x) care returneaza x*2. Apoi in main afiseaza rezultatele urmatoarelor compozitii:

De calculat:

a) patrat(succ(3))
b) dublu(patrat(2))
c) succ(dublu(patrat(1)))
d) patrat(patrat(2))

Indicii:

  • Evalueaza din interior spre exterior
  • a) succ(3) = 4, patrat(4) = 16
  • Verifica raspunsurile ruland programul!

Exercitiul 3 (Nivel performanta) - Scope si debugging

Cerinta: Urmatorul program are 3 greseli legate de scope si apelare. Gaseste-le, explica de ce sunt greseli si corecteaza-le:

void calcul() {
    int rezultat = 42;
}

int aduna(int a, int b) {
    return a + b;
}

int main() {
    calcul();
    cout << rezultat;
    int s = aduna(5);
    cout << s;
}

Indicii:

  • Cauta variabile folosite in afara scope-ului lor
  • Verifica daca numarul de argumente corespunde cu numarul de parametri
  • Gandeste-te cum poti "scoate" un rezultat dintr-o functie void

Cuvinte cheie de folosit: scope, variabila locala, parametru, argument, return, apel, prototip, call stack

Ce ai invatat astazi

  • Ai invatat cum functioneaza apelarea unei functii
  • Acum stii exemplu simplu: main cheama o functie
  • Ai descoperit ce este call stack-ul
  • Ai explorat construim comportament complex din piese simple
  • Ai inteles o functie poate sa se apeleze pe ea insasi
  • Ai invatat rezultatul unei functii ca argument pentru alta
  • Acum stii fiecare functie are propriile variabile
  • Ai descoperit regula: defineste inainte de a apela

Urmatoarea lectie

Continua cu lectia urmatoare pentru a aprofunda cunostintele.

Continua →