Subprograme: definire și apel

Antetul, corpul, valoarea returnată și cum citești o cerință de tip „scrieți subprogramul" din enunțul de bac.

De ce

Un subprogram (funcție) este o bucată de program cu un nume, care poate fi apelată de oriunde. Rezolvă trei probleme:

Repetiția. Dacă testezi de cinci ori dacă un număr e prim, scrii algoritmul o dată și îl apelezi de cinci ori.

Claritatea. if (esteprim(x)) se înțelege dintr-o privire. Cinci linii cu bucle nu se înțeleg la fel de repede.

Cerința examenului. La Subiectul III scrie explicit „scrieți definiția completă a subprogramului …", deci nu ai de ales.

Forma

tip nume(listă de parametri) {
    // corpul
    return valoare;
}
int patrat(int x) {
    return x * x;
}
  • int: tipul valorii returnate.
  • patrat: numele cu care va fi apelată.
  • int x: parametrul, adică ce primește funcția.
  • return: ce trimite funcția înapoi. Tot el o și oprește.

Apelul:

int a = patrat(5);       // a devine 25
cout << patrat(a + 1);   // se calculează întâi a+1

Unde se scrie

O funcție trebuie să fie cunoscută înainte de a fi apelată. Cel mai simplu este s-o scrii înaintea lui main:

#include <iostream>
using namespace std;

int patrat(int x) {
    return x * x;
}

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

Funcții fără rezultat

Dacă funcția face ceva, dar nu are ce întoarce, tipul ei este void:

void afiseazaVector(int v[], int n) {
    for (int i = 1; i <= n; i++)
        cout << v[i] << " ";
    cout << "\n";
}

O apelezi ca pe o instrucțiune separată: afiseazaVector(v, n);. Dacă vrei să ieși mai devreme din ea, scrii return; fără valoare.

return oprește funcția

După ce se execută un return, restul funcției nu se mai execută. Pe asta se sprijină un tipar util:

bool esteprim(int n) {
    if (n < 2) return false;
    for (int d = 2; d * d <= n; d++)
        if (n % d == 0)
            return false;        // am aflat răspunsul, ies imediat
    return true;
}

Funcția are trei return-uri, dar la fiecare apel se execută exact unul.

Orice drum prin funcție trebuie să ajungă la un return. Dacă o ramură nu returnează nimic, funcția întoarce o valoare imprevizibilă. Compilatorul, în cel mai bun caz, îți dă doar un avertisment.

Parametri și argumente

Parametrii sunt numele din antet. Argumentele sunt valorile date la apel.

int suma(int a, int b) { return a + b; }   // a, b sunt parametri
...
cout << suma(3, x);                         // 3 și x sunt argumente

Argumentele trebuie să corespundă parametrilor ca număr și ca tip, în aceeași ordine. suma(3) nu compilează.

Variabile locale

O variabilă declarată în interiorul unei funcții există numai acolo:

int sumaCifrelor(int n) {
    int s = 0;              // s există doar în această funcție
    while (n > 0) {
        s += n % 10;
        n /= 10;
    }
    return s;
}

Două funcții pot avea fiecare câte un s, fără nicio legătură între ele. La fel, n de aici este o copie: bucla îl distruge, dar variabila din main rămâne neatinsă. Revenim la asta în lecția despre parametri.

Cum arată cerința la bac

Enunțul dă antetul și îți cere corpul. De exemplu:

> Subprogramul nrdiv primește prin parametrul n un număr natural nenul și returnează numărul divizorilor lui n.

Din text extragi tot ce-ți trebuie:

  • numele: nrdiv
  • parametrul: n, număr natural
  • ce returnează: un număr → tipul este int
int nrdiv(int n) {
    int cati = 0;
    for (int d = 1; d * d <= n; d++)
        if (n % d == 0) {
            cati++;
            if (d != n / d) cati++;
        }
    return cati;
}

Câteva reguli de punctaj care se uită des:

Respectă numele și ordinea parametrilor din enunț. Corectorul verifică antetul.

Nu citi și nu afișa nimic în subprogram, dacă enunțul nu cere. Un subprogram care face cin >> n în loc să folosească parametrul este greșit, chiar dacă „merge".

Nu folosi variabile globale dacă enunțul nu le menționează.

Descompunerea unei probleme

Se citesc n numere. Să se afișeze câte dintre ele au suma cifrelor un număr prim.

Cu două subprograme, programul principal rămâne scurt și se citește ușor:

int sumaCifrelor(int n) { ... }
bool esteprim(int n) { ... }

int main() {
    int n, x, cate = 0;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> x;
        if (esteprim(sumaCifrelor(x)))
            cate++;
    }
    cout << cate;
    return 0;
}

esteprim(sumaCifrelor(x)) se citește exact ca enunțul. Așa îți dai seama că ai descompus bine problema: numele funcțiilor spun ce face programul, fără să te uiți în corpul lor.

De reținut

  • Antetul dă tipul returnat, numele și parametrii. Corpul face treaba.
  • return întoarce valoarea și oprește funcția pe loc.
  • Fiecare drum prin funcție trebuie să ajungă la un return.
  • Variabilele locale există doar în funcția lor.
  • La bac: respectă antetul din enunț, nu citi și nu afișa în subprogram.