Perché esistono le funzioni
Una funzione è un blocco di codice con un nome che puoi eseguire quando vuoi chiamandola. Invece di ripetere la stessa logica in più punti, la scrivi una volta, le dai un nome e usi quel nome ovunque ti serva. In questo modo i programmi restano più corti, più leggibili e più facili da correggere: cambi la logica in un solo punto e ogni chiamante riceve l'aggiornamento.
Finora hai sempre chiamato una funzione: main. È il punto di ingresso da cui parte ogni programma C++. Ora scriverai le tue. I cicli che hai visto prima, come il for basato su range, spesso vivono dentro le funzioni, così puoi riusare un intero pezzo di logica tramite il suo nome.
Anatomia di una funzione
Ogni funzione ha quattro parti: un tipo di ritorno, un nome, una lista di parametri tra parentesi e un corpo tra graffe.
int add(int a, int b) { // tipo di ritorno | nome | parametri
return a + b; // corpo
}
intè il tipo di ritorno, cioè il tipo di valore che la funzione restituisce.addè il nome che usi per chiamarla.(int a, int b)sono i parametri, gli input forniti dal chiamante.- Le graffe contengono il corpo, il codice che viene eseguito quando la chiami.
Eccola in un programma completo. Definire la funzione sopra main significa che main può vederla quando la chiama.
La chiamata add(2, 3) esegue la funzione con a = 2 e b = 3, e l'intera espressione diventa il valore restituito. Puoi memorizzarlo in una variabile o usarlo direttamente dentro un'altra espressione, come fa la seconda riga con cout.
Restituire un valore
L'istruzione return fa due cose: restituisce un valore al chiamante e termina immediatamente la funzione. Il codice che segue un return eseguito non viene eseguito: il controllo torna subito al punto in cui la funzione era stata chiamata.
Il tipo del valore restituito deve corrispondere al tipo di ritorno dichiarato (o essere convertibile in esso). Una funzione dichiarata int dovrebbe restituire un int; non restituire nulla, o arrivare alla fine senza un return, è un bug per qualsiasi funzione non void.
Funzioni void
Non tutte le funzioni producono un valore. Quando una funzione si limita a fare qualcosa, come stampare un output o aggiornare uno stato, il suo tipo di ritorno è void. Una funzione void può usare un semplice return; per uscire prima, oppure arrivare fino alla graffa di chiusura.
Provare a usare il risultato di una funzione void, come in int x = greet("Ada");, è un errore di compilazione, perché non c'è alcun valore da assegnare. Un errore comune è scrivere return someValue; dentro una funzione void; anche questo viene rifiutato dal compilatore.
Dichiarazioni e definizioni
Il C++ legge un file dall'alto verso il basso, quindi di default una funzione deve comparire prima del codice che la chiama. Quando quest'ordine è scomodo, dividi la funzione in una dichiarazione (detta anche prototipo) e una definizione.
Una dichiarazione indica la firma della funzione e termina con un punto e virgola, senza corpo. Promette al compilatore: "questa funzione esiste, ecco come chiamarla". La definizione completa può arrivare dopo, anche dopo main.
Senza il prototipo alla riga 4, il compilatore incontrerebbe square(5) dentro main prima di aver mai visto square, e la compilazione fallirebbe. I prototipi sono anche il modo in cui i file header permettono a molti file sorgente di condividere le stesse funzioni. Nota che i nomi dei parametri in una dichiarazione sono facoltativi: int square(int); funziona altrettanto bene, perché al compilatore interessano solo i tipi.
Errori comuni
Alcune trappole colpiscono i principianti di continuo:
- Chiamare prima di dichiarare. Se ottieni un errore "
addwas not declared in this scope", la funzione è definita sotto la sua prima chiamata e non ha un prototipo. Sposta la definizione più in alto o aggiungi un prototipo. - Dimenticare il return. Arrivare alla fine di una funzione non
voidsenza unreturnè comportamento indefinito: il chiamante riceve spazzatura. Compila con gli avvisi attivi (-Wall) e il compilatore lo segnalerà. - Definire e chiamare. Una definizione ha un corpo tra graffe e nessun punto e virgola finale. Una dichiarazione ha il punto e virgola e nessun corpo. Confonderle, per esempio mettendo un punto e virgola subito dopo la lista dei parametri di una funzione che volevi definire, produce errori confusi.
- Ignorare il valore restituito.
add(2, 3);da solo su una riga compila, ma la somma calcolata viene buttata via in silenzio. Assicurati di usare davvero ciò che una funzione restituisce.
// Sembra una definizione, ma il ; di troppo la trasforma in una
// dichiarazione seguita da un blocco isolato, un errore di battitura frequente:
int triple(int n); // <- questo ; chiude l'istruzione
{
return n * 3; // n qui non è definito; questo blocco resta orfano
}
Prossimo: Parametri delle funzioni
Hai visto che le funzioni ricevono input attraverso la lista dei parametri, ma c'è molto di più. La prossima pagina approfondisce i parametri delle funzioni: passaggio per valore o per riferimento, argomenti predefiniti, parametri const e come la scelta influisce sulla possibilità della funzione di modificare i dati del chiamante.
Domande frequenti
Come si scrive una funzione in C++?
Dalle un tipo di ritorno, un nome, delle parentesi per i parametri e un corpo tra graffe: int add(int a, int b) { return a + b; }. Chiamala per nome con gli argomenti, come add(2, 3). Se la funzione non restituisce nulla, usa void come tipo di ritorno.
Qual è la differenza tra dichiarazione e definizione di una funzione in C++?
Una dichiarazione (o prototipo) comunica al compilatore il nome, il tipo di ritorno e i parametri di una funzione, e termina con un punto e virgola: int add(int a, int b);. Una definizione fornisce anche il corpo tra graffe. Puoi dichiarare una funzione prima di main e definirla dopo: la dichiarazione ti permette di chiamarla prima che compaia la definizione.
Cosa succede se una funzione C++ non restituisce un valore?
Per una funzione void, nulla: semplicemente termina. Ma per una funzione non void, arrivare alla fine senza un return è comportamento indefinito: il chiamante riceve un valore spazzatura e il programma può comportarsi male. La maggior parte dei compilatori lo segnala con un avviso; restituisci sempre un valore in ogni percorso di una funzione non void.