⚙️ Funcții și Subprograme

Capitol bonus — Declarare, parametri, apel și probleme BAC cu funcții

FuncțiiParametriValoare vs ReferințăVariabile globaleSubprograme BAC
📖 12 min
0
⚙️

Declarare, definire și apelul funcțiilor

🏠 Analogie: Funcția = Rețetă de gătit

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;
}
Prototip (declarație înainte de main): Dacă funcția este definită după main, trebuie declarată înainte:
int suma(int a, int b); — linie înainte de main, corp după.
Declarare

Anunță existența: int suma(int, int);

Definire

Corpul complet cu { ... }

Apel

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!
}
Atenție: Trecerea prin valoare creează o copie completă. Pentru vectori mari, această copiere este costisitoare în timp și memorie. Folosiți referință în acel caz.

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
Vectorii se transmit întotdeauna prin referință (implicit — numele unui vector este un pointer la primul element). Nu este necesar & pentru vectori.
Prin valoare (fără &)

Copie. Funcția nu modifică originalul. Mai sigur.

Prin referință (&)

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 timpuriu: Poți folosi 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();
}
Variabile globale
  • 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
Variabile locale
  • Declarate în interiorul unei funcții
  • Vizibile doar în acea funcție
  • Nu sunt inițializate automat!
  • Distruse la ieșirea din funcție
La BAC — regula de aur: Vectorii mari (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).
Variabila locală ascunde cea globală: Dacă ai o variabilă locală cu același nume ca una globală, cea locală o «umbrește» pe cea globală în acel bloc.
📚

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
La BAC este permis să folosești funcțiile din <algorithm> și <cmath> fără să le implementezi manual. Verifică mereu cerința problemei!
📝

Probleme tip BAC — funcții și subprograme

Problemă 1 (BAC 2023):

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.

✅ Soluție:
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
Problemă 2:

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.

✅ Soluție:
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);
}
Problemă 3:

Scrieți o funcție bool verificaPrimalitate(int n) care verifică dacă n este număr prim, fără a folosi ciurul.

✅ Soluție:
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)
Tipare frecvente la BAC cu funcții:
  • 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)