Programare Competitivă

Transmiterea parametrilor

Când apelăm o funcție cu parametri, valorile sunt trimise funcției. Dar cum ajung acolo și ce se întâmplă cu ele? Există două moduri fundamental diferite.


Transmiterea prin valoare

Implicit, în C++, parametrii sunt transmiși prin valoare. Asta înseamnă că funcția primește o copie a valorii.

#include <iostream>
using namespace std;

void mareste(int x) {
    x = x + 10;
    cout << "In functie: " << x << endl;
}

int main()
{
    int a = 5;
    mareste(a);
    cout << "In main: " << a << endl;

    return 0;
}
Output:
In functie: 15
In main: 5

Ce s-a întâmplat?

  • a din main are valoarea 5.
  • La apelul mareste(a), se creează o copie numită x cu valoarea 5.
  • Funcția modifică copia x la 15.
  • Dar a din main rămâne neschimbat la 5.
main:     a = 5  ──copie──>  functie:  x = 5
          a = 5               x = 15  (doar copia s-a schimbat)

Transmiterea prin referință

Dacă vrem ca funcția să modifice variabila originală, folosim referință - punem & după tipul parametrului.

#include <iostream>
using namespace std;

void mareste(int &x) {
    x = x + 10;
    cout << "In functie: " << x << endl;
}

int main()
{
    int a = 5;
    mareste(a);
    cout << "In main: " << a << endl;

    return 0;
}
Output:
In functie: 15
In main: 15

Ce s-a schimbat?

  • Parametrul int &x înseamnă: x este o referință la variabila originală.
  • Nu se face copie. x și a sunt același lucru - același loc în memorie.
  • Modificarea lui x modifică direct a.
main:     a = 5  ──referinta──>  functie:  x = a (acelasi loc)
          a = 15                 x = 15

Comparație directă

Prin valoare Prin referință
Sintaxă void f(int x) void f(int &x)
Ce primește Copie Referință la original
Modifică originalul? NU DA
Când folosim Când nu vrem să modificăm Când vrem să modificăm

Exemplu clasic: interschimbarea a două variabile

Varianta greșită (prin valoare)

void interschimba(int a, int b) {
    int aux = a;
    a = b;
    b = aux;
}

int main()
{
    int x = 3, y = 7;
    interschimba(x, y);
    cout << x << " " << y; // 3 7 - NU s-au schimbat!

    return 0;
}

Funcția interschimbă copiile, nu originalele.

Varianta corectă (prin referință)

void interschimba(int &a, int &b) {
    int aux = a;
    a = b;
    b = aux;
}

int main()
{
    int x = 3, y = 7;
    interschimba(x, y);
    cout << x << " " << y; // 7 3 - Corect!

    return 0;
}
Output:
7 3

Când folosim fiecare?

Prin valoare - când funcția doar citește datele

bool estePar(int n) {      // doar verificăm, nu modificăm
    return n % 2 == 0;
}

int patrat(int x) {        // calculăm, nu modificăm
    return x * x;
}

Prin referință - când funcția trebuie să modifice datele

void citeste(int &n) {     // citim valoarea și o punem în n
    fin >> n;
}

void sorteaza(int v[], int n) {  // sortăm vectorul (vectorii se transmit automat prin referință)
    // ...
}

Vectorii se transmit prin referință automat

Când transmitem un vector la o funcție, nu se face copie - funcția lucrează direct pe vectorul original. Nu trebuie &.

#include <iostream>
using namespace std;

void dubleaza(int v[], int n) {
    for (int i = 1; i <= n; i++)
        v[i] = v[i] * 2;
}

int v[101];

int main()
{
    int n = 5;
    v[1] = 3; v[2] = 7; v[3] = 2; v[4] = 9; v[5] = 4;

    dubleaza(v, n);

    for (int i = 1; i <= n; i++)
        cout << v[i] << " ";

    return 0;
}
Output:
6 14 4 18 8

Vectorul s-a modificat direct - fără &.

De ce? Numele unui vector este de fapt un pointer (adresa primului element). Când transmitem v, transmitem adresa - deci funcția lucrează pe aceleași date.


Parametri multipli - combinații

Putem combina parametri prin valoare și prin referință în aceeași funcție:

void calcul(int a, int b, int &suma, int &produs) {
    suma = a + b;
    produs = a * b;
}

int main()
{
    int s, p;
    calcul(3, 5, s, p);
    cout << "Suma: " << s << endl;
    cout << "Produs: " << p << endl;

    return 0;
}
Output:
Suma: 8
Produs: 15

a și b sunt prin valoare (doar le citim), suma și produs sunt prin referință (le modificăm).


Exemplu complet: CMMDC ca funcție

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

int cmmdc(int a, int b) {
    while (b != 0) {
        int r = a % b;
        a = b;
        b = r;
    }
    return a;
}

int main()
{
    int a, b;
    fin >> a >> b;
    fout << cmmdc(a, b);

    return 0;
}

Aici a și b sunt prin valoare - funcția le modifică intern (în buclă), dar originalele din main rămân intacte. Exact ce vrem.


Greșeli frecvente

1. Uitarea lui & când vrem să modificăm

void citeste(int n) {   // GREȘIT - n e copie
    fin >> n;            // citim în copie, originalul rămâne neschimbat
}

Trebuie void citeste(int &n).


2. Folosirea & când nu e nevoie

bool estePrim(int &n) { // INUTIL - nu modificăm n

Dacă nu modificăm parametrul, nu punem &. E mai clar și mai sigur.


3. Transmiterea unei constante prin referință

void f(int &x) { x = 10; }

f(5); // EROARE! Nu putem modifica constanta 5

Prin referință se poate transmite doar o variabilă, nu o valoare constantă.


4. Confuzia cu vectorii

void f(int &v[], int n) { // GREȘIT - vectorii nu au nevoie de &
void f(int v[], int n) {  // CORECT

Ce să reții

  • Prin valoare (int x): funcția primește o copie. Originalul nu se modifică.
  • Prin referință (int &x): funcția lucrează direct pe original. Modificările se păstrează.
  • Vectorii se transmit automat prin referință (fără &).
  • Folosim referință pentru: interschimbare, citire, funcții cu mai multe rezultate.
  • Folosim valoare pentru: calcule, verificări, funcții care doar citesc datele.