Funzioni
- #c++
- #programmazione
- #funzioni
In questa lezione
- 1. Introduzione
- 2. Sintassi
- 3. Valore di ritorno
- 4. Dichiarazione (prototipo) e definizione
- 5. Parametri per valore vs per riferimento
- 5.1 Passaggio per valore
- 5.2 Passaggio per riferimento
- 5.3 Riferimenti costanti
- 6. Parametri con valori di default
- 7. Overloading
- 8. Passare un array a una funzione
- 9. Visibilità delle variabili (scope)
- 10. Esercizi
1. Introduzione
Le funzioni in C++, e in generale in tutti i linguaggi di programmazione, sono blocchi di codice riutilizzabili a cui si dà un nome: possono essere pensate come dei “sottoprogrammi” che vengono richiamati da altre parti del programma.
Usare le funzioni permette di:
- riutilizzare il codice, senza doverlo riscrivere più volte;
- aumentare la leggibilità del programma, rendendo il
mainpiù corto e chiaro; - dividere la risoluzione di un problema complesso in sottoproblemi più semplici.
Nota
Anche main è una funzione: è quella speciale da cui parte l’esecuzione di ogni programma C++.
2. Sintassi
La sintassi generale di una funzione è:
tipo_ritorno nome_funzione(elenco_parametri) {
istruzioni;
return valore_restituito;
}
Dove:
- tipo_ritorno: il tipo del valore che la funzione restituisce (
int,double,bool,std::string, …), oppurevoidse non restituisce nulla; - nome_funzione: il nome della funzione, che segue le stesse regole dei nomi delle variabili;
- elenco_parametri: i dati che la funzione riceve in ingresso, ognuno preceduto dal suo tipo e separati da virgola. La lista può anche essere vuota.
Esempio:
#include <iostream>
using namespace std;
int somma(int a, int b) {
return a + b;
}
int main() {
int risultato = somma(3, 5);
cout << risultato << endl; // Output: 8
}
Nella chiamata somma(3, 5) i valori 3 e 5 sono gli argomenti, che vengono assegnati ai parametri a e b.
3. Valore di ritorno
- L’istruzione
returntermina la funzione e restituisce un valore al chiamante. - Il valore restituito deve essere compatibile con il tipo di ritorno dichiarato.
- In C++ il tipo di ritorno è obbligatorio: non può essere omesso.
- Se la funzione compie solo un’azione e non deve restituire nulla, si usa il tipo
void. In questo casoreturn;(senza valore) è facoltativo e serve solo per uscire in anticipo.
void saluta(string nome) {
cout << "Ciao " << nome << "!" << endl;
}
bool isPari(int n) {
return n % 2 == 0;
}
Attenzione
Se una funzione non void arriva alla fine senza eseguire un return, il comportamento è indefinito: il compilatore spesso dà solo un warning, ma il programma può restituire valori casuali. Assicurati che ogni percorso del codice restituisca un valore.
4. Dichiarazione (prototipo) e definizione
Il compilatore legge il codice dall’alto verso il basso: una funzione deve essere dichiarata prima di essere usata. Se vogliamo scrivere le funzioni dopo il main, dobbiamo prima dichiararne il prototipo, cioè la sola intestazione seguita da ;.
#include <iostream>
using namespace std;
int quadrato(int n); // prototipo (dichiarazione)
int main() {
cout << quadrato(4) << endl; // Output: 16
}
int quadrato(int n) { // definizione
return n * n;
}
5. Parametri per valore vs per riferimento
5.1 Passaggio per valore
Quando un parametro è passato per valore, la funzione riceve una copia della variabile: le modifiche fatte all’interno della funzione non si riflettono sulla variabile originale del chiamante.
5.2 Passaggio per riferimento
Per passare un parametro per riferimento si antepone & al nome del parametro. In questo caso la funzione lavora direttamente sulla variabile originale: ogni modifica fatta dentro la funzione è visibile anche al chiamante.
#include <iostream>
using namespace std;
void funzione(int valore, int &riferimento) {
valore++;
riferimento++;
}
int main() {
int x = 0;
int y = 0;
funzione(x, y);
cout << x << endl; // 0: x è passato per valore
cout << y << endl; // 1: y è passato per riferimento
}
Attenzione
Un parametro per riferimento non costante (int &) deve ricevere una variabile: una chiamata come funzione(x, 5) dà errore di compilazione, perché un valore letterale come 5 non può essere modificato.
Un esempio classico in cui il passaggio per riferimento è indispensabile è lo scambio di due variabili:
void scambia(int &a, int &b) {
int temp = a;
a = b;
b = temp;
}
5.3 Riferimenti costanti
Quando un parametro è “pesante” da copiare (ad esempio una string o un vector) ma la funzione non deve modificarlo, si usa un riferimento costante const &: si evita la copia e il compilatore impedisce modifiche accidentali.
void stampa(const string &testo) {
cout << testo << endl;
// testo = "altro"; // errore: testo è const
}
A differenza di int &, un const int & può ricevere anche valori letterali, ad esempio stampa("ciao").
6. Parametri con valori di default
Ai parametri si possono assegnare dei valori di default: se nella chiamata l’argomento viene omesso, si usa il valore predefinito. I parametri con default devono stare in fondo alla lista.
double potenza(double base, int esponente = 2) {
double risultato = 1;
for (int i = 0; i < esponente; i++) {
risultato *= base;
}
return risultato;
}
int main() {
cout << potenza(3) << endl; // 9 (esponente = 2)
cout << potenza(2, 3) << endl; // 8
}
Nota
Se usi un prototipo, il valore di default va scritto solo nel prototipo e non anche nella definizione.
7. Overloading
In C++ si possono definire più funzioni con lo stesso nome, purché abbiano una lista di parametri diversa (per numero o per tipo). Il compilatore sceglie quale chiamare in base agli argomenti passati.
int massimo(int a, int b) {
return (a > b) ? a : b;
}
double massimo(double a, double b) {
return (a > b) ? a : b;
}
int massimo(int a, int b, int c) {
return massimo(massimo(a, b), c);
}
Attenzione
Non basta cambiare solo il tipo di ritorno: due funzioni con gli stessi parametri e tipo di ritorno diverso generano un errore di compilazione.
8. Passare un array a una funzione
Un array passato a una funzione non viene copiato: la funzione lavora sull’array originale. Poiché la funzione non conosce la dimensione dell’array, questa va passata come parametro separato.
int sommaArray(const int arr[], int dim) {
int somma = 0;
for (int i = 0; i < dim; i++) {
somma += arr[i];
}
return somma;
}
int main() {
int numeri[] = {1, 2, 3, 4};
cout << sommaArray(numeri, 4) << endl; // Output: 10
}
Anche qui const indica che la funzione non modificherà gli elementi dell’array.
9. Visibilità delle variabili (scope)
- Le variabili dichiarate dentro una funzione (parametri compresi) sono locali: esistono solo durante l’esecuzione della funzione e non sono visibili dall’esterno.
- Due funzioni diverse possono avere variabili locali con lo stesso nome senza interferire tra loro.
void f() {
int x = 10; // x esiste solo dentro f
}
int main() {
f();
// cout << x; // errore: x non è visibile qui
}