Sei sulla pagina 1di 58

Funzioni

OVVERO, LA MODULARIZZAZIONE DEL CODICE


Partiamo da un esempio

 Scriviamo un programma che


lanci due dadi indipendenti finché
la somma dei due risultati non dà
come risultato 9 e ci scriva il
numero di lanci.
 Dopo avere stampato il numero, vogliamo
vedere quanti lanci occorrono per avere
come somma il numero 6.
Conclusioni
Cosa cambia tra i due
programmi? Il programma:
 Di fatto, nulla a parte il int lanci=0, somma;
valore della somma da const int VALORE = 9;
ottenere.
do
 I due programmi sono {
composti dalle stesse
istruzioni. int d1 = rand()%6+1;
int d2 = rand()%6+1;
lanci++;
somma = d1 + d2;
}
while (somma != VALORE);
cout << lanci << endl;
Introduciamo le funzioni

 In matematica, una funzione è “una relazione f


che ad ogni elemento x del dominio (detto
argomento della funzione) associa uno ed un solo
elemento del codominio. L’elemento assegnato
a x tramite f viene indicato con f(x)”.
 Le funzioni sono dei mattoncini che possiamo
utilizzare per costruire il nostro programma.
 A volte questi mattoncini sono già stati preparati
(funzioni predefinite)
 Altre volte siamo noi a doverli preparare (funzioni
definite dall’utente/programmatore)
Perché non possiamo
lasciare tutto nel main?
 Lavorando su una  Se una funzione serve in
piccola parte del più punti del nostro
problema (la funzione), programma, possiamo
possiamo focalizzarci scriverla una sola volta;
solo su quella e ridurre gli
 Riduce la complessità
errori;
del main, migliorando
 Ognuno può lavorare su quindi la leggibilità e la
una funzione mantenibilità del
singolarmente e poi codice.
riunire tutto per avere la
soluzione;
Funzioni predefinite

 Le funzioni predefinite  Funzioni matematiche:


sono già state scritte da  pow(x, y)
altri programmatori
prima di noi.  sqrt(x)…
Libreria <cmath>
 Per poterle utilizzare,
spesso dobbiamo  Funzioni sui caratteri:
caricare le librerie  tolower(c)
(collezioni di funzioni)
dove sono definite.  toupper(c)
 isalpha(c)
 isdigit(c)…
Libreria <cctype>
Funzioni definite dallo
sviluppatore
Funzioni che restituiscono Funzioni che non
un valore restituiscono un valore
 Funzioni che  Funzioni che terminano
restituiscono un valore senza restituire un valore
attraverso l’istruzione
 Non hanno un tipo di
return
ritorno
 Hanno un tipo di ritorno
 void (“nulla, vuoto”) è il
(detto anche tipo della
tipo da assegnare a
funzione)
queste funzioni, che
 Il main, finora, ha vengono quindi
sempre restituito 0. chiamate “funzioni void”
Schema di una funzione

<tipo> <nome> (<lista parametri>)


{
<Corpo della funzione>
}
 Il tipo della funzione può essere void per le
funzioni che non restituiscono o un tipo (int, float,
bool…) per le altre
 Il nome della funzione segue le stesse regole che
abbiamo usato per le variabili
 La lista dei cosiddetti parametri formali può
anche essere vuota (funzioni senza argomenti)
Esempio 1

void stampaCiao()
{
cout << “Ciao” << endl;
}
 In questo caso non abbiamo parametri e la
funzione non restituisce alcun valore
Esempio 2

void stampaNCiao(int n)
{
for(int i=1; i<=n; i++) {
cout << “Ciao” << endl;
}
}
 In questo caso abbiamo un parametro formale
(n) che usiamo nel corpo della funzione come
limite per il ciclo for.
 La funzione non restituisce nulla.
Dove vanno le cose?

 La soluzione più semplice è scrivere le definizioni


delle funzioni dopo le inclusioni delle librerie, ma
prima del main

void stampaNCiao(int n){



}

int main (){



}
Dove vanno le cose?

 Se le scrivessi dopo il main avrei un errore del tipo


“funzione was not declared in this scope”

 Questo perché, esattamente come per le


variabili, il programma deve prima conoscere
quali funzioni esistono, per poi poterle utilizzare.
Chiamare una funzione

 Una volta definita la funzione, possiamo


chiamarla dove ne abbiamo bisogno;
 La chiamata di funzione consiste nel nome della
funzione, seguito dalla lista dei parametri attuali.

stampaCiao(); //non ha parametri


stampaNCiao(5); //ha un valore come parametro
stampaNCiao(k); //ha una variabile come parametro
Parametri

Parametri formali Parametri attuali


 Usati nella definizione  Usati nella chiamata
della funzione; della funzione;
 Sono dei “segnaposto”  Sono gli argomenti reali
che possiamo usare nel che vengono forniti alla
corpo della funzione in funzione per svolgere il
attesa di sapere quale suo compito;
sarà l’argomento reale;
 Rimangono anche dopo
 Sono locali alla funzione, il termine della funzione
cioè spariscono quando chiamata, ma il loro
essa termina. valore potrebbe essere
cambiato.
Come avviene il
passaggio di parametri?
Nella funzione
void somma(int a, int b) {…}

Nel main
somma( 5, 7);

 Quando verrà eseguito il corpo di somma, il


parametro formale a varrà 5 e b varrà 7
Come avviene il
passaggio di parametri?
 Il numero ed il tipo dei parametri attuali (cioè
della chiamata) deve corrispondere al numero
ed al tipo dei parametri formali (cioè della
definizione).

 In caso contrario ottengo errori come:


- error: too many arguments to function
- error: too few arguments to function
Il chiamante

 Il chiamante di una funzione f è la funzione g che


contiene nel suo corpo la chiamata alla
funzione f.
 Può essere un’altra funzione (a volte la funzione
può anche chiamare se stessa) oppure il main.
 Visto che il main è una funzione, chi è il suo
chiamante? (Il Sistema Operativo)
Dichiarazione del
prototipo
 Uno dei vantaggi delle  Come posso sapere
funzioni è la possibilità di quali funzioni sono state
dividere il programma in definite, se si trovano in
moduli. file diversi?
 La divisione in moduli  Posso usare il prototipo
consente di separare della funzione.
anche fisicamente (in
file diversi) le varie parti
del programma.
Dichiarazione del
prototipo
 Il prototipo consiste nel  Esso informa il
tipo della funzione, nome programma che “esiste
e parametri, inserito prima una funzione che
del main. prende questi parametri
e restituisce questo
valore (se non è void)”.
void stampaNCiao(int n);
Dichiarazione del
prototipo
 Una volta scritti tutti i void stampaNCiao(int n);
prototipi, possiamo
(nell’ordine che
preferiamo, anche dopo int main()
il main), scrivere la {
definizione delle funzioni,
ripetendo il prototipo e corpo del main…
inserendo il corpo.
}
 La definizione deve
corrispondere al
prototipo. void stampaNCiao(int n)
{
corpo di stampaNCiao…
}
Funzioni che restituiscono
un valore
Funzioni che restituiscono
un valore
 Sono funzioni il cui  Il chiamante può:
risultato viene restituito  Salvare il risultato in una variabile
al chiamante.
float x=f(2)
 Restituiscono un solo
valore.  Stampare il risultato

 Vengono chiamate cout<<f(2)


all’interno di espressioni  Usarlo nei calcoli

float area = pi*pow(raggio,2.0)


 Usarle come parametri di altre funzioni
float x = pow(abs(base),exp)
 In pratica, dovunque posso usare una
variabile posso usare anche una
chiamata a funzione (che restituisca)
Il comando return

 Le funzioni che restituiscono un valore terminano


con una istruzione nella forma
return expr
 expr può essere:
 un valore costante (es. 7, 3.14, ‘g’, true, …),
 una variabile (paperino, somma, lanci,…)
 una espressione composta (3+8, costo-sconto,
prezzo*0.2, k >= 0…)
 una chiamata a funzione (che restituisca)
Il comando return

 expr viene prima valutata e poi restituita.


 Quando viene eseguita l’istruzione return, tutte le
successive vengono ignorate.
 È buona prassi avere un’unica istruzione return
che conclude la funzione (single exit-point)
Esercizio 1

 Scrivete una funzione che, presi come parametri


due numeri reali, restituisca il maggiore dei due.
Prima possibile soluzione

float maggiore(float a, float b) {


float m;
if(a >= b) {
m = a;
}
else {
m = b;
}
return m;
}
Seconda possibile soluzione

float maggiore(float a, float b) {


if(a >= b) {
return a;
}
else {
return b;
}
}
Terza possibile soluzione

float maggiore(float a, float b) {


if(a >= b) {
return a;
}
return b;
}
Esercizio 2

 Scrivete una funzione che, preso come


argomento un carattere restituisca se è o no una
vocale.
 Successivamente, usate la funzione appena
definita all’interno di un programma che legga
da tastiera una sequenza di caratteri e conti il
numero di vocali presenti.
Una possibile soluzione

bool vocale(char a) {
if ((a == ‘a’) || (a == ‘e’) || (a == ‘i’)
|| (a == ‘o’) || (a == ‘u’))
{
return true;
}
else
{
return false;
}
}
Esercizio 3

 Scrivete una funzione che, usando la funzione


maggiore definita nell’esercizio precedente,
restituisca il maggiore di tre numeri.
Una possibile soluzione

float maggioreDiTre( float a,


float b,
float c)
{
return maggiore(a, maggiore(b,c));
}
Esercizi 4 – 5 – 6

 Scrivete un programma che calcoli attraverso


opportune funzioni l’area ed il perimetro di un
rettangolo dati i suoi lati.
 Scrivete una funzione che abbia come parametri
il prezzo di vendita e la percentuale di sconto e
restituisca il prezzo scontato.
 Scrivete una funzione che prenda come
parametro il voto di una verifica e restituisca un
valore booleano che dica se è sufficiente.
Funzioni che non
restituiscono un valore
Restituire VS Non restituire

Cos’hanno in comune Cos’hanno di diverso


 Hanno una struttura  Nel prototipo, il tipo
simile della funzione è sostituito
 Hanno un prototipo
da void

 Hanno un corpo  Il comando return può


essere usato, ma senza
 Possono essere definite alcun argomento
prima o dopo il main (se (return;)
definite dopo, ci vuole il
prototipo)  Non vengono chiamate
in espressioni, ma come
 Possono avere o non istruzioni autonome
avere parametri formali
Uso delle funzioni void

 Quando abbiamo una attività da svolgere che


non deve fornire un risultato al chiamante, ad es.
sequenze di comandi che terminano con una
stampa.
Esercizio 1

 Scrivere un programma che stampi k volte il


nome dell’utente
Proposta di soluzione

void stampaKNome(int k, string n)


{
for (int i=0; i<k; i++)
{
cout << n << endl;
}
}
Esercizio 2
Scrivere una funzione che prenda come parametro
un punteggio (compreso tra 0 e 100) e stampi il
corrispettivo grado di superamento, secondo la
tabella:

Punteggio Grado
>= 95 Super
>=90 A
>=80 B
>=70 C
>=60 D
Altrimenti F
Passaggio di parametri
Diversi passaggi di
parametri?
 Abbiamo visto che, quando chiamiamo una
funzione, i parametri attuali della chiamata
vengono associati ai parametri formali.
 Ci sono vari modi di associare i parametri attuali a
quelli formali:
 Valore
 Valore-risultato
 Risultato
 Riferimento/indirizzo
 Costante
 Nome
Diversi passaggi di
parametri?
 Abbiamo visto che, quando chiamiamo una
funzione, i parametri attuali della chiamata
vengono associati ai parametri formali.
 Ci sono vari modi di associare i parametri attuali a
quelli formali:
 Valore
 Valore-risultato
 Risultato
 Riferimento/indirizzo
 Costante
 Nome
Il passaggio per VALORE

 Il passaggio per valore è quello che abbiamo


usato finora, senza sapere che si chiama così.
 Con questo sistema il VALORE del parametro
attuale viene COPIATO nella zona di memoria del
parametro formale.
Cosa succede in memoria

void myfunction(int k) {
k += 4;
cout << k << “ “;
M
}
e
m
int main(){ o
int x = 15; r
i
myfunction(x);
a
cout << x;
}
x 15
Cosa succede in memoria

void myfunction(int k) {
k += 4;
cout << k << “ “;
M
}
e
m
int main(){ o
int x = 15; r
i
myfunction(x);
a
cout << x; 15
k
}
x 15
Cosa succede in memoria

void myfunction(int k) {
k += 4;
cout << k << “ “;
M
}
e
m
int main(){ o
int x = 15; r
i
myfunction(x);
a
cout << x; 19
k
}
x 15
Cosa succede in memoria

void myfunction(int k) {
k += 4;
cout << k << “ “;
M
}
e
m
int main(){ o
int x = 15; r
i
myfunction(x);
a
cout << x; 19
k
}
19 x 15
Cosa succede in memoria

void myfunction(int k) {
k += 4;
cout << k << “ “;
M
}
e
m
int main(){ o
int x = 15; r
i
myfunction(x);
a
cout << x;
}
19 15 x 15
Il passaggio per
RIFERIMENTO
 Prima di introdurre il concetto di passaggio per
riferimento (o per indirizzo), bisogna prima capire
come si costruisce una variabile in memoria.
 Quando il programma esegue una istruzione di
dichiarazione, la variabile dichiarata viene
allocata, cioè viene riservata una zona di
memoria che servirà a contenere il valore della
variabile.
 Ogni zona della memoria ha un indirizzo,
rappresentato come un valore esadecimale tipo
0xABCD1234
Variabili ed Indirizzi

 Ogni variabile, quindi,  Quando faccio


possiede un proprio riferimento ad una
indirizzo, diverso da tutte variabile, ad esempio
le altre variabili cout << x, il computer la
dichiarate. interpreta così:
 Il programma memorizza E’ richiesta la variabile x
quindi l’abbinamento L’indirizzo di x è 0xABCD1234
tra il nome della
variabile ed il suo Stampa il contenuto della
indirizzo in memoria cella di memoria 0xABCD1234
Come si effettua un
passaggio per riferimento
Nella definizione Nel chiamante
 Si aggiunge & al tipo del  Non cambia
parametro, che diventa assolutamente nulla!
una referenza myfunction(x);

void myfunction(int& k)
{
k += 4;
cout << k << “ “
}
Cosa succede in memoria

void myfunction(int& k) { Memoria


k += 4;
cout << k << “ “; x 15
}

int main(){
int x = 15;
myfunction(x);
cout << x;
}
Cosa succede in memoria

void myfunction(int& k) { Memoria


k += 4;
cout << k << “ “; k x 15
}

int main(){
int x = 15;
myfunction(x);
cout << x;
}
Cosa succede in memoria

void myfunction(int k) void myfunction(int& k)

k x 15

Notate come k non sia


una nuova variabile con lo
stesso valore di x, ma un
altro nome per x
k 15

x 15
Cosa succede in memoria

void myfunction(int& k) { Memoria


k += 4;
cout << k << “ “; k x 19
}

int main(){
int x = 15;
myfunction(x);
cout << x;
}
Cosa succede in memoria

void myfunction(int& k) { Memoria


k += 4;
cout << k << “ “; k x 19
}

int main(){ 19
int x = 15;
myfunction(x);
cout << x;
}
Cosa succede in memoria

void myfunction(int& k) { Memoria


k += 4;
cout << k << “ “; x 19
}

int main(){ 19 19
int x = 15;
myfunction(x);
cout << x;
}
Cosa succede in memoria

void myfunction(int k) void myfunction(int& k)

k 19 k x 19

x 15

19 15 19 19