Programare Competitivă

Funcții - noțiuni de bază

Până acum, tot codul nostru a fost scris în funcția main(). Dar pe măsură ce programele devin mai complexe, avem nevoie să împărțim codul în bucăți mai mici, fiecare cu un rol clar.

Aceste bucăți se numesc funcții (sau subprograme).


De ce avem nevoie de funcții?

Imaginează-ți că ai un program care verifică dacă un număr e prim în 5 locuri diferite. Fără funcții, copiezi aceleași 10 linii de 5 ori. Cu funcții, le scrii o singură dată și le apelezi de câte ori vrei.

Funcțiile ne ajută să:

  • Evităm repetarea codului
  • Organizăm programul în bucăți logice
  • Testăm fiecare parte separat
  • Citim codul mai ușor

Primul exemplu

#include <iostream>
using namespace std;

void salut() {
    cout << "Buna ziua!" << endl;
}

int main()
{
    salut();
    salut();
    salut();

    return 0;
}
Output:
Buna ziua!
Buna ziua!
Buna ziua!

Ce se întâmplă?

  1. Am definit funcția salut() - un bloc de cod cu un nume.
  2. În main(), am apelat funcția de 3 ori.
  3. La fiecare apel, programul “sare” la codul funcției, îl execută, apoi revine.

Structura unei funcții

tip_returnat  nume_functie(parametri) {
    // corpul funcției
}
Parte Ce înseamnă
tip_returnat Ce tip de valoare returnează funcția (void dacă nu returnează nimic)
nume_functie Numele pe care îl alegem (ca la variabile)
parametri Datele pe care le primește funcția (opțional)
corpul Instrucțiunile care se execută la apel

Funcții void (fără valoare returnată)

O funcție void face ceva dar nu întoarce un rezultat. E ca un ordin: “afișează asta”, “desenează aia”.

void afiseazaLinie() {
    cout << "-------------------" << endl;
}

int main()
{
    afiseazaLinie();
    cout << "Titlu" << endl;
    afiseazaLinie();

    return 0;
}
Output:
-------------------
Titlu
-------------------

Funcții cu parametri

Parametrii sunt datele pe care le trimitem funcției. Funcția le folosește în corpul ei.

void afiseazaNume(char nume[]) {
    cout << "Salut, " << nume << "!" << endl;
}

int main()
{
    afiseazaNume("Ana");
    afiseazaNume("Mihai");
    afiseazaNume("Elena");

    return 0;
}
Output:
Salut, Ana!
Salut, Mihai!
Salut, Elena!

Mai mulți parametri

void afiseazaSuma(int a, int b) {
    cout << a << " + " << b << " = " << a + b << endl;
}

int main()
{
    afiseazaSuma(3, 5);
    afiseazaSuma(10, 20);

    return 0;
}
Output:
3 + 5 = 8
10 + 20 = 30

Funcții care returnează o valoare

O funcție poate calcula ceva și întoarce rezultatul cu return.

int dublu(int x) {
    return x * 2;
}

int main()
{
    int a = dublu(5);
    cout << a << endl;
    cout << dublu(13) << endl;

    return 0;
}
Output:
10
26

Cum funcționează return?

  • return valoare; oprește funcția și trimite valoare înapoi la locul apelului.
  • Tipul valorii trebuie să corespundă cu tipul declarat al funcției (int returnează int).
int maxim(int a, int b) {
    if (a > b)
        return a;
    else
        return b;
}

int main()
{
    cout << maxim(7, 12) << endl;
    cout << maxim(100, 3) << endl;

    return 0;
}
Output:
12
100

Funcții care returnează bool

Foarte utile pentru verificări:

bool estePar(int n) {
    if (n % 2 == 0)
        return true;
    else
        return false;
}

int main()
{
    if (estePar(8))
        cout << "8 este par" << endl;

    if (!estePar(7))
        cout << "7 nu este par" << endl;

    return 0;
}
Output:
8 este par
7 nu este par

Prescurtare: funcția estePar se poate scrie și return n % 2 == 0; - expresia n % 2 == 0 este deja true sau false.


Ordinea în program

Funcțiile trebuie definite înainte de a fi apelate. De obicei le punem deasupra lui main():

#include <iostream>
using namespace std;

// Funcțiile aici, deasupra lui main
int patrat(int x) {
    return x * x;
}

int main()
{
    cout << patrat(5);
    return 0;
}
Declarare vs Definire

Alternativ, putem pune doar declarația (prototipul) deasupra și definiția dedesubt:

int patrat(int x); // declarație (prototip)

int main()
{
cout << patrat(5);
return 0;
}

int patrat(int x) { // definiție
return x * x;
}

Ambele variante sunt corecte. La concursuri, cel mai simplu e să pui funcțiile deasupra lui main.


Exemplu complet: verificare număr prim

#include <fstream>
using namespace std;
ifstream fin("date.in");
ofstream fout("date.out");

bool estePrim(int n) {
    if (n < 2) return false;
    for (int d = 2; d * d <= n; d++) {
        if (n % d == 0) return false;
    }
    return true;
}

int main()
{
    int n;
    fin >> n;

    for (int i = 2; i <= n; i++) {
        if (estePrim(i))
            fout << i << " ";
    }

    return 0;
}

date.in:

30

date.out:

2 3 5 7 11 13 17 19 23 29

Funcția estePrim ascunde complexitatea verificării. main rămâne curat și ușor de citit.


Greșeli frecvente

1. Apelarea funcției fără paranteze

salut;    // GREȘIT - nu apelează funcția
salut();  // CORECT

2. Uitarea lui return într-o funcție non-void

int dublu(int x) {
    int rez = x * 2;
    // lipsește: return rez;
}

Funcția nu întoarce nimic - rezultatul e imprevizibil.


3. Tipul parametrilor nu corespunde

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

suma(3.5, 2.1); // trimitem double, funcția așteaptă int - se pierd zecimalele

4. Funcția definită după main()

int main()
{
    salut(); // EROARE - salut nu e definit încă
    return 0;
}

void salut() { cout << "Salut"; }

Pune funcțiile deasupra lui main.


Ce să reții

  • O funcție este un bloc de cod cu un nume, pe care îl putem apela de mai multe ori.
  • void - funcție care nu returnează nimic.
  • return - trimite o valoare înapoi la locul apelului.
  • Parametrii sunt datele pe care funcția le primește.
  • Funcțiile se definesc deasupra lui main().
  • Cu funcții, codul devine mai curat, mai organizat și mai ușor de citit.