← Programmazione
C++ · Funzioni

Funzioni e Passaggio Parametri

Anatomia di una funzione, tutti i modi di passare parametri — valore, riferimento, const riferimento, puntatore — array mono e bidimensionali, con simulatore interattivo.

🎓 3° anno ITIS/SIA 🔧 Simulatore parametri ⏰ ~40 min
void raddoppia ( int & x) { x *= 2 ; // x NEL CHIAMANTE cambia

Anatomia di una funzione

Una funzione è un blocco di codice con un nome che esegue un compito specifico e può essere richiamato più volte. Permette di evitare la duplicazione del codice e di suddividere un problema complesso in parti gestibili.

C++ · anatomia
// PROTOTIPO (dichiarazione): annuncia l'esistenza della funzione al compilatore
// Va messo prima del main se la definizione compare dopo
int somma(int a, int b);

int main() {
    int ris = somma(3, 5);   // CHIAMATA: passa 3 e 5 come argomenti
    cout << ris;             // stampa 8
    return 0;
}

// DEFINIZIONE: il corpo effettivo della funzione
int somma(int a, int b) {
    return a + b;   // restituisce il risultato al chiamante
}
📌 Prototipo vs definizione

Il prototipo dice al compilatore il nome, il tipo di ritorno e i tipi dei parametri — basta per poterla chiamare. La definizione contiene il corpo. Se la definizione precede il main, il prototipo è superfluo. Nei progetti multi-file il prototipo va nell'header (.h), la definizione nel sorgente (.cpp).

Funzioni void e return

C++ · void e return
// void: non restituisce nulla
void saluta(string nome) {
    cout << "Ciao, " << nome << "!" << endl;
    // return; si usa solo per uscire anticipatamente (es. controllo errori)
    // in assenza di condizioni particolari, non va scritto
}

// con valore di ritorno: calcola e restituisce un risultato al chiamante
float media(float a, float b) {
    return (a + b) / 2.0f;
}

int main() {
    saluta("Mario");
    float m = media(7.5f, 8.0f);
    cout << "Media: " << m << endl;  // 7.75
    return 0;
}

Passaggio di parametri

Quando si chiama una funzione si passano i valori su cui deve lavorare. Il C++ offre quattro modi, ognuno con effetti diversi sulla variabile originale nel chiamante.

Per valore

La funzione riceve una copia del valore. Qualsiasi modifica alla copia resta locale e non si riflette sulla variabile originale.

C++ · per valore
void prova(int x) {       // x è una COPIA del parametro
    x = x * 2;            // modifica solo la copia locale
    cout << "dentro: " << x << endl;
}

int main() {
    int a = 5;
    prova(a);
    cout << "fuori: " << a << endl;  // stampa 5 — a NON è cambiato
    return 0;
}

Per riferimento (&)

L'operatore & nella firma trasforma il parametro in un alias della variabile originale. Non viene creata nessuna copia: la funzione opera direttamente sulla variabile del chiamante.

C++ · per riferimento
void raddoppia(int& x) {  // & = riferimento: x è un alias di chi viene passato
    x = x * 2;            // modifica DIRETTAMENTE la variabile del chiamante
}

void scambia(int& a, int& b) {
    int tmp = a; a = b; b = tmp;
}

int main() {
    int n = 5;
    raddoppia(n);
    cout << n << endl;          // 10 — n è cambiato

    int x = 3, y = 7;
    scambia(x, y);
    cout << x << " " << y;     // 7 3
    return 0;
}

const riferimento (const tipo&)

Passare per riferimento evita di copiare il dato — utile con tipi grandi come string o struct. Aggiungere const impedisce alla funzione di modificare il valore: si ottiene efficienza senza rischio di modifiche accidentali.

C++ · const riferimento
// senza const: efficiente ma la funzione potrebbe modificare s
void stampa(string& s) { cout << s; }

// con const: zero copie + garanzia che s non venga modificata
void stampa(const string& s) {
    cout << s << endl;
    // s = "altro";  // ❌ errore di compilazione: s è const
}

int main() {
    string nome = "Mario Rossi";
    stampa(nome);   // zero copie, nessuna modifica possibile
    return 0;
}
💡 Quando usare const riferimento

Per tipi piccoli (int, float, char) il passaggio per valore è sufficiente — copiarli è istantaneo. Per tipi grandi (string, struct, array) usa const tipo& se non devi modificare il dato, e tipo& se devi modificarlo.

Per puntatore (*)

La funzione riceve l'indirizzo della variabile. L'effetto è lo stesso del riferimento — si modifica l'originale — ma la sintassi è diversa: nella chiamata si usa & per passare l'indirizzo, e dentro la funzione * per accedere al valore. È lo stile del C, ancora diffuso.

C++ · per puntatore
void raddoppia(int* p) {
    if (p == nullptr) return;  // controlla sempre prima di dereferenziare
    *p = *p * 2;              // * = vai all'indirizzo e modifica il valore
}

int main() {
    int n = 5;
    raddoppia(&n);             // & = passa l'indirizzo di n
    cout << n << endl;         // 10
    return 0;
}

Array come parametro

Array monodimensionali

Quando passi un array a una funzione, il compilatore lo tratta automaticamente come un puntatore al primo elemento (tecnicamente: array decay). L'array non viene copiato: la funzione lavora direttamente sull'originale. La dimensione deve essere passata come parametro separato.

C++ · array monodimensionale
// queste tre firme sono equivalenti per il compilatore:
void stampa(int arr[], int n)      // forma più leggibile — preferita
void stampa(int* arr, int n)      // equivalente: arr diventa puntatore
void stampa(int arr[10], int n)   // la dimensione [10] è ignorata

// funzione che LEGGE l'array (const = nessuna modifica accidentale)
void stampa(const int arr[], int n) {
    for (int i = 0; i < n; i++)
        cout << arr[i] << " ";
    cout << endl;
}

// funzione che MODIFICA l'array
void raddoppia(int arr[], int n) {
    for (int i = 0; i < n; i++)
        arr[i] *= 2;   // modifica l'array originale
}

int main() {
    int v[5] = {1, 2, 3, 4, 5};
    stampa(v, 5);      // 1 2 3 4 5
    raddoppia(v, 5);
    stampa(v, 5);      // 2 4 6 8 10
    return 0;
}
⚠ sizeof non funziona sugli array passati come parametro

Dentro la funzione, sizeof(arr) restituisce la dimensione del puntatore (8 byte su 64 bit), non quella dell'array. La dimensione va sempre passata come parametro separato n.

Array bidimensionali

Con le matrici la regola cambia: la prima dimensione può essere omessa, la seconda è obbligatoria. Il compilatore ne ha bisogno per calcolare la posizione di ogni elemento in memoria: mat[i][j] corrisponde a *(mat + i * COL + j), quindi senza sapere quante colonne ha ogni riga non può fare i conti.

C++ · array bidimensionale
// prima dimensione [] omessa: ok
// seconda dimensione [3]: OBBLIGATORIA
void stampa(int mat[][3], int righe) {
    for (int i = 0; i < righe; i++) {
        for (int j = 0; j < 3; j++)
            cout << mat[i][j] << "	";
        cout << endl;
    }
}

void azzera(int mat[][3], int righe) {
    for (int i = 0; i < righe; i++)
        for (int j = 0; j < 3; j++)
            mat[i][j] = 0;   // modifica la matrice originale
}

int main() {
    int m[2][3] = {{1,2,3}, {4,5,6}};
    stampa(m, 2);   // 1 2 3 / 4 5 6
    azzera(m, 2);
    stampa(m, 2);   // 0 0 0 / 0 0 0
    return 0;
}
⚠ La seconda dimensione è parte del tipo

Una funzione che accetta int mat[][3] accetta solo matrici con 3 colonne. Per matrici con numero di colonne variabile si usano altre tecniche (array di puntatori o allocazione dinamica) che vanno oltre questo livello.

Tabella comparativa

ModalitàSintassi firmaChiamataModifica originale?Quando usare
Per valorevoid f(int x)f(a) No — copiaLettura, tipi piccoli
Per riferimentovoid f(int& x)f(a) Sì — aliasOutput, swap, modifica
const riferimentovoid f(const string& s)f(a) No — protettoLettura di tipi grandi
Per puntatorevoid f(int* p)f(&a) Sì — via indirizzoStile C, puntatore nullable
Array 1Dvoid f(int arr[], int n)f(v, 5) Sì — decayElaborazione array
Array 2Dvoid f(int m[][3], int r)f(m, 2) Sì — decayElaborazione matrici

🔧 Simulatore passaggio parametri

Imposta un valore iniziale, scegli la modalità e osserva cosa succede alla variabile nel chiamante prima, durante e dopo la chiamata.

🔧 Simulatore Parametri Prima · Durante · Dopo
Valore iniziale di a
Operazione nella funzione
👤 Chiamante (main)
a =
Prima della chiamata
chiama
⚙️ Dentro la funzione
x =
👤 Chiamante — DOPO la chiamata
a =
Scegli modalità, imposta il valore e premi Esegui.