Declarare, definire și apelul funcțiilor
O funcție este ca o rețetă: o scriem o singură dată și o putem folosi (apela) de ori câte ori vrem. Are ingrediente (parametri de intrare) și produce un rezultat (valoarea returnată). Scopul principal este să evităm duplicarea codului.
Structura unei funcții
// tip_returnat nume_functie(parametri) { corp } int suma(int a, int b) { // parametri prin valoare return a + b; // valoarea returnată } bool estePar(int n) { return n % 2 == 0; // true dacă n este par } void afiseazaLinie() { // void = nu returnează nimic cout << "----------------" << endl; } int main() { int rezultat = suma(3, 7); // apel funcție → rezultat = 10 cout << rezultat << endl; if (estePar(4)) cout << "4 este par" << endl; afiseazaLinie(); // apel funcție void return 0; }
int suma(int a, int b); — linie înainte de main, corp după.
Anunță existența: int suma(int, int);
Corpul complet cu { ... }
Execuție: suma(3, 7)
Parametri prin valoare
Când transmitem un parametru prin valoare, funcția primește o copie a argumentului. Modificările din funcție nu afectează variabila originală.
void dublu(int x) { x = x * 2; // modificăm copia, nu originalul cout << x << endl; // afișează 10 } int main() { int a = 5; dublu(a); // trimitem valoarea 5, nu variabila cout << a << endl; // afișează 5 — a nu s-a modificat! }
Parametru cu valoare implicită (C++)
int putere(int baza, int exp = 2) { // exp implicit = 2 int rez = 1; for (int i = 0; i < exp; i++) rez *= baza; return rez; } cout << putere(3, 3); // 27 — folosim exp=3 cout << putere(5); // 25 — folosim exp implicit=2
Parametri prin referință
Parametrii prin referință (marcați cu &) permit funcției să modifice direct variabila originală. Nu se creează copie.
void dublu(int &x) { // & = referință la variabila originală x = x * 2; } int main() { int a = 5; dublu(a); // a devine 10 cout << a << endl; // 10 — a s-a modificat! }
Cazul clasic: funcție de schimbare (swap)
void schimba(int &a, int &b) { int temp = a; a = b; b = temp; } int x = 3, y = 7; schimba(x, y); // acum x=7, y=3
Citire în funcție — referință la n
void citesteVector(int v[], int &n) { // n prin referință cin >> n; for (int i = 1; i <= n; i++) cin >> v[i]; } int v[100001], n; citesteVector(v, n); // n se actualizează în main
& pentru vectori.
Copie. Funcția nu modifică originalul. Mai sigur.
Alias. Funcția modifică originalul. Mai eficient.
Tipul returnat — void vs non-void
Funcții care returnează o valoare
int maxim(int a, int b) { return a > b ? a : b; // returnează int } bool ePrim(int n) { if (n < 2) return false; for (int i = 2; i * i <= n; i++) if (n % i == 0) return false; return true; // returnează bool } double medie(int v[], int n) { int s = 0; for (int i = 1; i <= n; i++) s += v[i]; return (double)s / n; // returnează double }
Funcții void (fără valoare returnată)
void afiseazaVector(int v[], int n) { for (int i = 1; i <= n; i++) cout << v[i] << " "; cout << endl; } // nu există return (sau return; fără valoare) void sorteaza(int v[], int n) { for (int i = 1; i < n; i++) for (int j = 1; j <= n-i; j++) if (v[j] > v[j+1]) swap(v[j], v[j+1]); }
return în orice punct al funcției pentru a ieși mai devreme — util pentru validare la început.
Variabile locale și globale
int n; // variabilă GLOBALĂ — vizibilă în tot programul int v[100001]; // inițializat automat cu 0 (global) void calcul() { int suma = 0; // variabilă LOCALĂ — există doar în calcul() for (int i = 1; i <= n; i++) suma += v[i]; // accesează globalele n și v cout << suma; } // suma e distrusă la ieșirea din calcul() int main() { cin >> n; for (int i = 1; i <= n; i++) cin >> v[i]; calcul(); }
- Declarate în afara oricărei funcții
- Vizibile în tot fișierul
- Inițializate automat cu 0
- Rămân în memorie pe toată durata programului
- Declarate în interiorul unei funcții
- Vizibile doar în acea funcție
- Nu sunt inițializate automat!
- Distruse la ieșirea din funcție
int v[100001]) se declară global, nu local în main(). Variabilele locale sunt pe stivă (limitat la ~1 MB), globalele în segment de date (mai mult spațiu).
Subprograme predefinite — biblioteci standard
C++ oferă o serie de funcții gata scrise pe care le poți folosi incluzând header-ul corespunzător.
<algorithm> — cel mai util la BAC
#include <algorithm> sort(v + 1, v + n + 1); // sortare crescătoare O(n log n) sort(v + 1, v + n + 1, greater<int>()); // sortare descrescătoare int mx = *max_element(v+1, v+n+1); // maximul din vector int mn = *min_element(v+1, v+n+1); // minimul din vector reverse(v+1, v+n+1); // inversează vectorul int m = max(a, b); // maximul a doi scalari int mn2 = min(a, b);
<cmath> — funcții matematice
#include <cmath> sqrt(16.0) // → 4.0 (rădăcina pătrată) pow(2, 10) // → 1024 (2 la puterea 10) abs(-5) // → 5 (valoare absolută) floor(3.7) // → 3 (rotunjire în jos) ceil(3.2) // → 4 (rotunjire în sus)
<string> — metode pe string
#include <string> string s = "Hello"; s.length() // 5 — lungimea s.substr(1, 3) // "ell" — subsecvență de la 1, lungime 3 s.find("ll") // 2 — poziția primei apariții s + " World" // "Hello World" — concatenare s[0] // 'H' — acces caracter la indice 0
Demo-uri interactive — funcții și subprograme (5)
Rulează demo-urile de mai jos pentru a vedea exact diferențele între valoare, referință (&), afisare și returnare.
Aceeași funcție, două variante: fără & (copie) și cu & (modifică originalul).
Compară o funcție void afiseazaSuma(a,b) cu int suma(a,b).
Vezi cum „alias-ul” referinței urmărește aceeași variabilă.
Simulare de „shadowing”: variabila locală ascunde variabila globală.
Model pentru putere(baza, exp=2): apel cu/sau fără al doilea argument.
Probleme rezolvate — funcții
5 probleme clasice cu funcții — separarea responsabilităților, parametri prin valoare/referință, return.
bool estePrim(int n) care întoarce true dacă n este prim, false altfel. Apelează funcția în main pentru a verifica un număr citit.17
DA
💡 Vezi soluția
bool estePrim(int n) { if (n < 2) return false; for (int d = 2; (long long)d*d <= n; d++) if (n % d == 0) return false; return true; } int main() { int n; cin >> n; cout << (estePrim(n) ? "DA" : "NU"); }
Idee: verificăm divizori până la √n. Folosim d*d <= n ca să evităm sqrt (mai rapid). Funcția izolează logica — codul din main rămâne curat.
int sumCifre(int n) care întoarce suma cifrelor lui n. Apoi afișează suma cifrelor pentru toate numerele dintr-un vector citit.4 123 5 99 1234
6 5 18 10
💡 Vezi soluția
int sumCifre(int n) { int s = 0; while (n) { s += n % 10; n /= 10; } return s; } int main() { int n; cin >> n; for (int i = 0, x; i < n; i++) { cin >> x; cout << sumCifre(x) << ' '; } }
Idee: funcția modifică n local (parametru prin valoare) — n-ul din main rămâne neschimbat. Returnul oferă rezultatul.
void interschimba(int &a, int &b) care interschimbă valorile celor două variabile. Demonstreaz-o în main.3 7
7 3
💡 Vezi soluția
void interschimba(int &a, int &b) { int t = a; a = b; b = t; } int main() { int x, y; cin >> x >> y; interschimba(x, y); cout << x << ' ' << y; }
Cheia: & înainte de nume = parametru prin referință. Modificările făcute în funcție afectează variabilele din main. Fără &, modificările sunt locale și se pierd.
5 3 1 4 1 9
min=1 max=9
💡 Vezi soluția
void minMax(int v[], int n, int &mn, int &mx) { mn = mx = v[0]; for (int i = 1; i < n; i++) { if (v[i] < mn) mn = v[i]; if (v[i] > mx) mx = v[i]; } } int main() { int n, v[105]; cin >> n; for (int i = 0; i < n; i++) cin >> v[i]; int mn, mx; minMax(v, n, mn, mx); cout << "min=" << mn << " max=" << mx; }
De ce referință: o funcție poate returna o singură valoare prin return. Cu parametrii prin referință putem „returna" mai multe rezultate. Vectorul v[] se transmite implicit prin referință (pointer).
long long fib(int n, long long memo[]) care folosește un tablou memo (parametru) pentru a evita recalcularea. Apel cu memo inițializat la −1.30
832040
💡 Vezi soluția
long long fib(int n, long long memo[]) { if (n <= 1) return n; if (memo[n] != -1) return memo[n]; return memo[n] = fib(n-1, memo) + fib(n-2, memo); } int main() { int n; cin >> n; long long memo[95]; for (int i = 0; i < 95; i++) memo[i] = -1; cout << fib(n, memo); }
De ce parametri și nu global: funcția devine reutilizabilă și testabilă (poți o apela cu memo-uri diferite). Vectorul memo[] e transmis implicit prin referință — modificările persistă între apeluri.
Probleme tip BAC — funcții și subprograme
Scrieți o funcție int cmmdc(int a, int b) care returnează cel mai mare divizor comun al lui a și b, folosind algoritmul lui Euclid.
int cmmdc(int a, int b) { while (b != 0) { int r = a % b; a = b; b = r; } return a; // O(log min(a,b)) } // cmmmc(a, b) = a / cmmdc(a, b) * b
Scrieți o funcție void citeste(int v[], int &n) și o funcție void afiseaza(int v[], int n), apoi un subprogram void elimina(int v[], int &n, int p) care elimină elementul de la poziția p.
void citeste(int v[], int &n) { cin >> n; for (int i = 1; i <= n; i++) cin >> v[i]; } void afiseaza(int v[], int n) { for (int i = 1; i <= n; i++) cout << v[i] << " "; cout << endl; } void elimina(int v[], int &n, int p) { for (int i = p; i < n; i++) v[i] = v[i+1]; n--; } int main() { int v[100001], n; citeste(v, n); elimina(v, n, 3); // elimină elem. de la poziția 3 afiseaza(v, n); }
Scrieți o funcție bool verificaPrimalitate(int n) care verifică dacă n este număr prim, fără a folosi ciurul.
bool verificaPrimalitate(int n) { if (n < 2) return false; for (int i = 2; (long long)i * i <= n; i++) if (n % i == 0) return false; return true; } // Complexitate: O(√n)
- Scrieți subprogramul
void citire(int v[], int &n)și apelați-l în main - Scrieți subprogramul care modifică vectorul (prin referință sau direct)
- Scrieți subprogramul care returnează un scalar (cmmdc, verificare, calcul)
- Structurați programul: citire → prelucrare → afișare (funcții separate)