Primul item de la Subiectul III la BAC informatică îți cere să scrii definiția completă a unui subprogram în C++. În modelul actual valorează 10 puncte (verifică întotdeauna ultimul model oficial pe subiecte.edu.ro). Algoritmul cerut e de obicei unul clasic — cifre, divizori, numere prime, CMMDC — dar punctele se pierd des pe detalii: un nume schimbat, un parametru transmis greșit, un return lipsă sau un caz limită uitat. Mai jos ai o metodă în cinci pași și două exemple rezolvate complet.
Cum arată enunțul
Enunțul are aproape mereu aceeași structură: „Subprogramul X are un/doi parametri, … prin care primește …, și … prin care furnizează …. Subprogramul returnează …. Scrieți definiția completă a subprogramului.” Urmează un exemplu cu valori concrete. Fiecare bucată din propoziția asta se traduce direct în cod:
| În enunț | În cod |
|---|---|
| „are parametrul n, prin care primește un număr natural” | int n (prin valoare) |
| „parametrul s, prin care furnizează…” | int &s (prin referință) |
| „subprogramul returnează…” | funcție cu tip + return |
| nu returnează nimic, doar furnizează prin parametri | void |
| „primește un tablou v cu n elemente” | int v[], int n |
Ordinea parametrilor din antet trebuie să fie exact ordinea din enunț, iar numele subprogramului se copiază literă cu literă, inclusiv majusculele (nrDiv nu e același lucru cu nrdiv).
Metoda în cinci pași
- Scrie antetul primul, direct din enunț: tip, nume, parametri în ordine, cu
&doar unde ți se cere să „furnizezi”. - Alege tipul datelor după intervalele din enunț: până la 109 încape în
int; dacă un rezultat poate depăși aproximativ 2·109 (de exemplu un produs), foloseștelong long. - Scrie algoritmul cu variabile locale inițializate explicit.
- Verifică pe exemplul din enunț, de mână, cu un mic tabel de valori.
- Verifică un caz limită: cea mai mică valoare din interval, numărul 0, un vector cu un singur element, situația în care „nu există” rezultat.
main, de #include sau de citiri, iar subprogramul nu trebuie să citească sau să afișeze nimic dacă enunțul nu spune explicit asta. Datele vin prin parametri, rezultatele pleacă prin return sau prin referințe.Exemplul 1: subprogram care returnează o valoare
Subprogramul cifreImpare are un singur parametru, n, prin care primește un număr natural (n ∈ [0, 109]). Subprogramul returnează numărul format din cifrele impare ale lui n, în ordinea în care apar în acesta, sau −1 dacă n nu are nicio cifră impară. Exemplu: pentru n = 23541 se returnează 351.
Antetul rezultă imediat: int cifreImpare(int n). Parcurgem cifrele de la dreapta la stânga, deci ca să păstrăm ordinea, fiecare cifră impară găsită trebuie pusă în fața celor deja găsite. Pentru asta ținem o putere a lui 10, p.
int cifreImpare(int n)
{
int rez = 0, p = 1;
while (n > 0)
{
int c = n % 10;
if (c % 2 == 1)
{
rez = c * p + rez;
p = p * 10;
}
n = n / 10;
}
if (p == 1) return -1; // nu s-a găsit nicio cifră impară
return rez;
}
Verificare pe exemplu, n = 23541:
| cifra c | impară? | rez | p |
|---|---|---|---|
| 1 | da | 1 | 10 |
| 4 | nu | 1 | 10 |
| 5 | da | 51 | 100 |
| 3 | da | 351 | 1000 |
| 2 | nu | 351 | 1000 |
Cazuri limită: pentru n = 0 bucla nu se execută, p rămâne 1 și se returnează −1 — corect, pentru că 0 e cifră pară. Pentru n = 2468, la fel. De ce nu testăm rez == 0? Pentru că aici ar funcționa, dar în variante ale problemei (de exemplu „cifrele pare”) numărul format poate fi chiar 0, iar testul pe p sau un contor separat e sigur în orice situație.
Exemplul 2: rezultat furnizat prin referință
Subprogramul nrDiv are doi parametri: n, prin care primește un număr natural (n ∈ [1, 109]), și k, prin care furnizează numărul de divizori ai lui n. Exemplu: pentru n = 12, k = 6.
Nu se spune „returnează”, ci „furnizează prin parametrul k”, deci funcția e void, iar k are &. Cum n poate fi până la 109, nu merge să încercăm toți divizorii de la 1 la n; mergem până la √n și numărăm divizorii în perechi (d și n/d).
void nrDiv(int n, int &k)
{
k = 0;
for (int d = 1; d * d <= n; d++)
if (n % d == 0)
{
k++; // d
if (d != n / d) k++; // perechea lui, n / d
}
}
Pentru n = 12: d = 1 (perechea 12), d = 2 (perechea 6), d = 3 (perechea 4), deci k = 6. Pentru n = 16: d = 1 (16), d = 2 (8), d = 4 (perechea e tot 4, se numără o singură dată), deci k = 5. Linia k = 0; e esențială: k e o variabilă din afara subprogramului și poate conține orice înainte de apel. Iar d * d <= n nu depășește int, pentru că d nu trece de aproximativ 31 623.
Ce urmărește, în general, corectorul
Baremele oficiale acordă de obicei puncte separate pentru antetul corect, pentru algoritmul propriu-zis și pentru furnizarea rezultatului, plus punctaj pentru declararea corectă a datelor. Detaliile diferă de la an la an, așa că citește baremul fiecărei simulări pe care o rezolvi. Concluzia practică: un antet corect și un return corect aduc puncte chiar dacă algoritmul are o scăpare, iar un antet greșit te poate costa puncte chiar dacă restul e perfect.
Greșeli care costă puncte
- Citire în subprogram:
cin >> n;în corpul funcției suprascrie valoarea primită ca parametru. - Afișare în loc de returnare:
cout << rez;nu e echivalent cureturn rez;. &lipsă la parametrul prin care „furnizezi” rezultatul.returnîn interiorul buclei pus prea devreme, astfel încât funcția se oprește după primul pas.- Algoritm ineficient pe intervale mari, când enunțul sugerează valori de ordinul 109.
- Exemplul din enunț neverificat: e cel mai ieftin test pe care îl ai.
De reținut
- ● „Primește” → parametru prin valoare; „furnizează” → parametru prin referință (
&); „returnează” → funcție cu tip șireturn. - ● Numele și ordinea parametrilor se copiază exact din enunț.
- ● Se cere doar definiția: fără
main, fără citiri și afișări, dacă nu se precizează altfel. - ● Inițializează explicit orice rezultat furnizat prin referință.
- ● Testează pe exemplul din enunț și pe un caz limită (0, valoarea minimă, „nu există”).
- ● Alege tipul după intervale și un algoritm eficient (de exemplu până la √n la divizori).