Funzioni e Argomenti in Linguaggio C

Abbiamo visto, in precedenza, che un parametro di una funzione è una variabile dichiarata nella definizione di una funzione, mentre un argomento di una funzione è il valore effettivo passato alla funzione quando viene invocata.

Adesso dobbiamo scendere nel dettaglio e studiare meglio come il linguaggio C gestisce i parametri e gli argomenti di una funzione.

In particolare, è fondamentale capire il concetto di passaggio per valore degli argomenti e come il compilatore C gestisce la conversione di argomenti.

Parametri e Argomenti di Funzione

Già in precedenza, nella lezione sulla definizione di funzioni, abbiamo introdotto il concetto di parametro e argomento di una funzione.

Adesso entriamo nel dettaglio e riprendiamo la differenza, più che altro concettuale, tra parametro e argomento.

Un parametro è una variabile dichiarata nella definizione di una funzione. I parametri sono utilizzati per passare informazioni alla funzione. Un parametro è un placeholder per un valore che verrà passato alla funzione quando verrà invocata.

Un argomento è il valore effettivo che viene passato alla funzione quando viene invocata. Gli argomenti sono i valori che vengono passati alla funzione quando viene chiamata.

Un argomento di funzione può essere il nome di una variabile o il risultato di un'espressione.

Per chiarire, prendiamo la definizione della funzione somma:

int somma(int x, int y) {
    return x + y;
}

In questo caso, x e y sono parametri della funzione somma. Quando invochiamo la funzione somma, dobbiamo passare due argomenti che verranno assegnati ai parametri x e y.

int a = 10;

int risultato = somma(a, (5 * 2) + 3);

Nell'esempio sopra, a e (5 * 2) + 3 sono argomenti della funzione somma. Quando la funzione somma viene invocata, il valore di a viene assegnato a x e il valore di (5 * 2) + 3 viene assegnato a y. Come si può notare, in un caso l'argomento è il nome di una variabile, nell'altro è il risultato di un'espressione.

Molto spesso, nei testi di programmazione, si fa riferimento a parametri e argomenti come se fossero la stessa cosa. In realtà, è importante capire la differenza tra i due concetti per comprendere meglio come funzionano le funzioni in linguaggio C.

Passaggio per Valore

Un'importante dettaglio che riguarda gli argomenti di una funzione è il passaggio per valore.

In linguaggio C, gli argomenti di una funzione vengono passati per valore. Questo significa che, quando si invoca una funzione, viene passata una copia del valore dell'argomento alla funzione. Tale copia viene assegnata al parametro corrispondente.

Per chiarire, prendiamo la funzione incrementa:

int incrementa(int x) {
    return x + 1;
}

Quando invochiamo la funzione incrementa, il valore dell'argomento viene copiato nel parametro x. Ad esempio, nel codice che segue:

int a = 10;
int b = incrementa(a);

Il valore di a (che è 10) viene copiato nel parametro x della funzione incrementa. Quindi, la funzione incrementa lavorerà con una copia del valore di a, non con il valore effettivo di a.

La conseguenza è che qualsiasi modifica apportata ad un parametro di una funzione non influirà sul valore dell'argomento originale. Ad esempio:

int incrementa(int x) {
    x = x + 1;
    return x;
}

int a = 10;
int b = incrementa(a);

printf("a: %d\n", a); // Stampa: a: 10
printf("b: %d\n", b); // Stampa: b: 11

Sebbene la funzione incrementa modifichi il valore del parametro x, il valore di a rimane invariato.

Il passaggio per valore è un concetto importante e bisogna sempre tenerlo presente quando si lavora con funzioni in linguaggio C.

Definizione

Passaggio per Valore

In linguaggio C, tutti gli argomenti di funzione vengono passati per valore. Questo significa che viene passata una copia del valore dell'argomento alla funzione. Qualsiasi modifica apportata ai parametri di una funzione non influirà sul valore dell'argomento originale.

I parametri di una funzione si comportano, effettivamente, come variabili inizializzate con il valore dell'argomento corrispondente.

Il fatto che gli argomenti vengono passati per valore ha dei vantaggi e degli svantaggi.

Un primo vantaggio è che, dal momento che un parametro di una funzione può essere modificato senza influire sull'argomento passato, possiamo utilizzare i parametri come variabili all'interno della funzione riducendo, così, la quantità di variabili ausiliarie di cui abbiamo bisogno.

Ad esempio, supponiamo di voler realizzare una funzione che calcola la potenza di un numero intero. Possiamo scrivere la funzione in questo modo:

int potenza(int base, int esponente) {
    int risultato = 1;

    for (int i = 0; i < esponente; i++) {
        risultato *= base;
    }

    return risultato;
}

Tuttavia, dal momento che il parametro esponente non è altro che una copia del valore dell'argomento passato, possiamo modificarlo senza influire sull'argomento originale. Quindi, possiamo riscrivere la funzione senza la necessità di usare una variabile i, in questo modo:

int potenza(int base, int esponente) {
    int risultato = 1;

    while (esponente-- > 0) {
        risultato *= base;
    }

    return risultato;
}

In questo modo, abbiamo eliminato la variabile i e utilizzato direttamente il parametro esponente come contatore del ciclo while.

Lo svantaggio principale del passaggio per valore è che, dato che il linguaggio C ammette un solo valore di ritorno, non possiamo scrivere funzioni che restituiscono più di un valore. Tuttavia, questo limite può essere superato grazie all'uso dei puntatori che vedremo nelle prossime lezioni.

Conversione di Argomenti

Il compilatore C, in generale, si aspetta che il tipo degli argomenti passati ad una funzione corrisponda al tipo dei parametri della funzione stessa. Se il tipo degli argomenti non corrisponde al tipo dei parametri, il compilatore genererà un errore.

Questo non è, però, sempre vero. In alcuni casi, il compilatore C è in grado di convertire automaticamente gli argomenti passati ad una funzione nel tipo richiesto dai parametri.

Le regole di conversione dipendono dal fatto che il compilatore, quando trova la chiamata di una funzione, abbia già incontrato il prototipo della funzione stessa.

  • Se il compilatore ha incontrato il prototipo della funzione, allora è in grado di convertire automaticamente gli argomenti passati alla funzione nel tipo richiesto dai parametri. Questo è possibile perché il compilatore sa già come convertire gli argomenti in modo corretto.

    Ad esempio, se abbiamo il prototipo della funzione somma:

    int somma(int x, int y);
    

    Il compilatore sa che somma richiede due argomenti di tipo int. Se passiamo alla funzione due argomenti di tipo float, il compilatore sarà in grado di convertire automaticamente i valori float in int.

    float a = 10.5;
    float b = 20.3;
    
    int risultato = somma(a, b);
    

    In questo caso, il compilatore convertirà i valori float a e b in int prima di passarli alla funzione somma.

  • Se il compilatore non ha incontrato il prototipo della funzione allora la situazione dipende dal fatto se il compilatore rispetti lo standard C89 oppure supporti lo standard dal C99 in poi.

    • Standard C89: se il compilatore rispetta lo standard C89, allora effettua una conversione implicita:

      1. Un argomento float viene convertito in un double;
      2. Qualsiasi altro tipo viene convertito in un int (laddove possibile).
    • Standard C99 e successivi: se il compilatore supporta lo standard C99 o successivi, allora genera un errore.

Nota

Usare sempre i prototipi e mai affidarsi alla conversione implicita

Per evitare errori e comportamenti inaspettati, è sempre meglio dichiarare i prototipi delle funzioni e assicurarsi che il tipo degli argomenti passati corrisponda al tipo dei parametri della funzione.

In Sintesi

In questa lezione abbiamo approfondito i concetti di parametro di una funzione e argomento di una funzione che avevamo già introdotto nelle lezioni precedenti.

Abbiamo visto che un parametro è una variabile dichiarata nella definizione di una funzione, mentre un argomento è il valore effettivo passato alla funzione quando viene invocata.

Abbiamo anche discusso del passaggio per valore degli argomenti in linguaggio C. Gli argomenti vengono passati per valore, il che significa che viene passata una copia del valore dell'argomento alla funzione. Questo ha delle implicazioni sul comportamento delle funzioni e sulle modifiche ai parametri.

Infine, abbiamo parlato della conversione di argomenti. Il compilatore C è in grado di convertire automaticamente gli argomenti passati ad una funzione nel tipo richiesto dai parametri, ma è sempre meglio dichiarare i prototipi delle funzioni per evitare errori e comportamenti inaspettati.

Nella prossima lezione approfondiremo il caso in cui vogliamo passare come argomento un array ad una funzione.