Funzioni e Valori di Ritorno in Linguaggio C
Apprendiamo in questa lezione come funzionano i valori di ritorno in linguaggio C. In particolare, vediamo come restituire un valore da una funzione e come utilizzare l'istruzione return
.
Valori di ritorno
Abbiamo detto che, in informatica, si usa distinguere tra procedure e funzioni.
Una procedura può essere vista come una macro-istruzione, ossia un aggregato di istruzioni più semplici che vengono eseguite sequenzialmente.
Viceversa, una funzione è un'entità che prende in ingresso dei parametri, effettua delle operazioni su di essi e restituisce un valore. Possiamo vedere una funzione come una scatola nera che prende in ingresso dei dati e restituisce un risultato.
Il linguaggio C non effettua, come fanno altri linguaggi, una distinzione netta tra procedure e funzioni. In C una procedura è semplicemente una funzione che non restituisce alcun valore, ossia è una funzione di tipo void
. Viceversa, una funzione è una funzione che restituisce un valore.
Procedure in Linguaggio C
Una Procedura in linguaggio C è una funzione con tipo di ritorno void
:
void nome_procedura(lista_parametri) {
// corpo della procedura
}
Una procedura è anche nota come funzione void
nel gergo del linguaggio C.
Funzioni in Linguaggio C
Una Funzione in linguaggio C è una funzione con tipo di ritorno diverso da void
:
tipo_di_ritorno nome_funzione(lista_parametri) {
// corpo della funzione
return valore_di_ritorno;
}
Nelle lezioni precedenti abbiamo già visto come definire delle funzioni che restituiscono un risultato. Adesso studiamo i dettagli di questa operazione utilizzando l'istruzione return
.
L'istruzione return
Una qualunque funzione non void
, ossia che restituisca un risultato, deve necessariamente contenere un'istruzione return
che restituisca il valore di ritorno.
La sintassi dell'istruzione return
è la seguente:
return espressione;
L'espressione dell'istruzione return
può essere una costante, una variabile o un'espressione più complessa.
Ad esempio, è molto comune trovare espressioni return
così scritte:
return 0;
return x;
return x + y;
Anche espressioni più complesse possono essere usate. Non è raro, ad esempio, avere espressioni condizionali all'interno di un'istruzione return
:
return (x > y) ? x : y;
In questo esempio, viene valutata l'espressione (x > y)
. Se il risultato è vero, viene restituito il valore di x
, altrimenti viene restituito il valore di y
.
Un punto importante dell'istruzione return
è che, una volta eseguita, il controllo della funzione passa al chiamante. In altre parole, l'istruzione return
termina l'esecuzione della funzione e restituisce il controllo al chiamante.
Poiché un'istruzione return
può apparire in qualunque punto del corpo della funzione, è possibile che una funzione contenga più di un'istruzione return
. In questo caso, solo una delle istruzioni return
verrà eseguita.
Prendiamo un esempio per chiarire questo concetto:
int massimo(int x, int y) {
if (x > y) {
return x;
} else {
return y;
}
}
In questo esempio, la funzione massimo
restituisce il massimo tra due numeri interi x
e y
. Se x
è maggiore di y
, viene restituito x
, altrimenti viene restituito y
. Per cui la funzione ha due istruzioni return
, ma solo una di esse verrà eseguita.
Avremmo potuto scrivere la funzione di sopra anche in questo modo:
int massimo(int x, int y) {
if (x > y) {
return x;
}
return y;
}
La funzione massimo
così realizzata, funziona comunque perché, se x
è maggiore di y
, viene restituito x
e la funziona termina. Ossia, la funzione non continua la propria esecuzione dopo l'istruzione return x;
quindi l'istruzione return y;
non verrà mai eseguita.
Istruzione return
L'istruzione return
è utilizzata per restituire un valore da una funzione. L'istruzione return
è seguita da un'espressione che rappresenta il valore di ritorno:
return espressione;
Quando viene eseguita un'istruzione return
, il controllo della funzione passa al chiamante.
Una funzione con tipo di ritorno diverso da void
deve necessariamente avere un'istruzione return
al proprio interno. In caso contrario, il controllo della funzione raggiunge la fine della stessa ed il valore di ritorno sarà casuale. Il problema potrebbe diventare catastrofico nel momento in cui si cerca di utilizzare il valore di ritorno di una funzione che non ha restituito alcun valore. In tal caso il comportamento del programma diventa impredicibile.
Ad esempio:
int somma(int x, int y) {
int risultato = x + y;
/* ERRORE: manca l'istruzione return */
}
In questo esempio, la funzione somma
calcola la somma tra due numeri interi x
e y
, ma non restituisce alcun valore. Se proviamo a memorizzarne il risultato in una variabile, il risultato sarà casuale e il comportamento del programma sarà imprevedibile:
int risultato = somma(3, 4);
/* Da qui in poi il comportamento del programma è imprevedibile */
Molti compilatori, tra cui il compilatore GCC, avvertono dell'errore di mancanza di return
in una funzione non void
. Ad esempio, il compilatore GCC restituisce il seguente errore:
error: control reaches end of non-void function [-Werror=return-type]
Questo errore, in realtà, è molto più comune di quello che sembra. Ad esempio, quando una funzione contiene una serie di istruzioni if-else
e non tutte le possibili casistiche sono state considerate, potrebbe presentarsi il caso in cui nessuna delle istruzioni return
venga eseguita. In tal caso, il compilatore avverte dell'errore.
Ad esempio:
int massimo(int x, int y) {
if (x > y) {
return x;
} else if (y > x) {
return y;
}
/* ERRORE: manca l'istruzione return */
}
In questo esempio sembra a prima vista che abbiamo considerato tutte le casistiche possibili, ma in realtà non è così. Se x
e y
sono uguali, nessuna delle due istruzioni return
verrà eseguita. In tal caso, il compilatore avverte dell'errore.
Mancanza di return
in una funzione non void
Una funzione con tipo di ritorno diverso da void
deve necessariamente contenere un'istruzione return
. In caso contrario, il controllo della funzione raggiunge la fine della stessa ed il valore di ritorno sarà casuale. Il comportamento del programma diventa impredicibile.
Bisogna sempre controllare che tutte le casistiche possibili siano state considerate e che tutte le possibili casistiche abbiano un'istruzione return
associata.
In ogni caso, i compilatori moderni avvertono dell'errore di mancanza di return
in una funzione non void
.
Tipo del Valore di Ritorno
In linea di principio, l'espressione dell'istruzione return
deve essere dello stesso tipo del tipo di ritorno previsto dalla funzione.
Ad esempio:
float funzione() {
return 3.14f;
}
In tal caso l'espressione 3.14f
è di tipo float
, che è lo stesso tipo di ritorno della funzione funzione
.
Nel caso in cui ciò non avviene si possono verificare due casi:
-
Il compilatore tenta una conversione implicita se possibile.
Ad esempio, se la funzione ha come tipo di ritorno
int
ma l'espressione dell'istruzionereturn
è di tipofloat
, il compilatore effettua una conversione implicita dafloat
aint
.int funzione() { return 3.14f; }
Questo risulta possibile perché il compilatore è in grado di convertire un valore
float
in un valoreint
. Ovviamente questa conversione viene segnalata come warning dal compilatore in quanto convertire un valorefloat
in un valoreint
comporta la perdita della parte decimale.Il compilatore GCC, ad esempio, restituisce il seguente warning:
warning: implicit conversion from 'float' to 'int'
Viceversa, se la conversione non comporta perdita di informazione, il compilatore non restituisce alcun warning.
-
Se la conversione implicita non è possibile, il compilatore restituisce un errore.
Prendiamo l'esempio che segue:
int funzione() { return "ciao"; }
In questo caso, l'espressione dell'istruzione
return
è una stringa, ossia un array di caratteri. Il tipo di ritorno della funzionefunzione
èint
, che è un tipo di dato diverso dachar[]
. In questo caso, il compilatore restituisce un errore:error: return makes integer from pointer without a cast
Ricapitolando:
Tipo del Valore di Ritorno
L'espressione dell'istruzione return
deve essere dello stesso tipo del tipo di ritorno previsto dalla funzione. In caso contrario:
- Il compilatore tenta una conversione implicita se possibile.
- Se la conversione implicita non è possibile, il compilatore restituisce un errore.
return
e funzioni void
Abbiamo detto che una funzione void
non restituisce alcun valore. Quindi, in questo caso, un'istruzione return
non è necessaria.
Tuttavia, è possibile utilizzare un'istruzione return
in una funzione void
. In tal caso, l'istruzione return
termina l'esecuzione della funzione e restituisce il controllo al chiamante. Quando si usa per questi casi, l'istruzione return
non deve essere seguita da alcuna espressione.
La sintassi diventa:
return;
L'utilità di usare una return
in una funzione void
è che, in caso di errori o condizioni particolari, si può terminare l'esecuzione della funzione e restituire il controllo al chiamante.
Ad esempio, supponiamo di avere una funzione void
che pretende in ingresso un numero intero positivo. Se il numero in ingresso è negativo, possiamo terminare l'esecuzione della funzione e restituire il controllo al chiamante:
void funzione(int x) {
if (x < 0) {
printf("Errore: il numero deve essere positivo\n");
return;
}
// corpo della funzione
}
In questo esempio, se il numero x
è minore di zero, viene stampato un messaggio di errore e l'esecuzione della funzione termina.
return
in Funzioni void
Una funzione void
può contenere al proprio interno un'istruzione return
. In tal caso, l'istruzione return
termina l'esecuzione della funzione e restituisce il controllo al chiamante.
Usata in questo contesto l'istruzione return
non deve essere seguita da alcuna espressione:
return;
In Sintesi
Abbiamo approfondito, in questa lezione, l'uso dell'istruzione return
in una funzione:
- L'istruzione
return
è utilizzata per restituire un valore da una funzione; - Una funzione con tipo di ritorno diverso da
void
deve necessariamente contenere almeno un'istruzionereturn
; - L'espressione dell'istruzione
return
deve essere dello stesso tipo del tipo di ritorno previsto dalla funzione; - Se la conversione implicita non è possibile, il compilatore restituisce un errore;
- Una funzione
void
non restituisce alcun valore, ma può contenere un'istruzionereturn
per terminare in modo anticipato l'esecuzione della funzione stessa.