int - E-Learning per il Corso di Laurea in Matematica

Sottoprogrammi e funzioni
Sottoprogrammi, funzioni,
passaggio dei parametri.
Sottoprogrammi
• Salvo quelle elementari (ad es.
operazioni aritmetiche) ad ogni
operazione corrisponde un programma
• Un’operazione più volte ripetuta in un
programma può essere realizzata da
un’unica porzione di codice che viene
eseguita ogni volta che serve: un
sottoprogramma.
GOSUB del BASIC
100 LET X = 3
110 LET Y = 5
120 GOSUB 400
130 PRINT MASSIMO
...
400 IF X > Y THEN 430
410 LET MASSIMO = Y
420 GOTO 440
430 LET MASSIMO = X
440 RETURN
Quando si esegue la linea 120, prima di saltare alla linea 400 viene
memorizzato il punto da cui si proviene; quando si segue RETURN (l. 440)
si torna all’istruzione immediatamente successiva a quella ricordata (l.
130).
I parametri
• La X e la Y sono usate per l’
ingresso, MASSIMO per
l’uscita sottoprogramma:
cosa accade se vengono
impiegate altrove nel
programma?
100 LET X = 3
110 LET Y = 5
120 GOSUB 400
130 PRINT MASSIMO
...
400 IF X > Y THEN 430
410 LET MASSIMO = Y
420 GOTO 440
430 LET MASSIMO = X
440 RETURN
L’uso di semplici variabili per “passare” i valori verso un
sottoprogramma provoca la perdita dei loro valori
precedenti: non è possibile usarle con un significato
autonomo nel resto del programma.
Funzioni
• Una funzione è un sottoprogramma
con parametri, ossia con variabili
proprie attraverso le quali riceve
l’ingresso (e può restituire l’uscita).
Funzioni: sintassi
<tipo> <nome funzione> ([<lista param.>]) {<corpo>}
<tipo> è il tipo del valore restituito (void se non viene
restituito alcun valore)
<lista param.> (eventualmente vuota) è una sequenza
di dichiarazioni di variabili, separate da virgole,
della forma: <tipo> <nome variabile>
Tipo del
valore
int max (int x, int y)
{
corpo
if (x > y) return x
else return y;
}
Lista parmetri
Il tipo di x (e di y)
deve essere
coerente con
quello di ritorno
Funzioni: dichiarazioni,
definizioni e chiamate
Le funzioni compaiono in tre modi:
• Dichiarazioni (il corpo è ridotto a “;”):
double sqrt(double x);
• Definizioni: sono dichiarazioni seguite dal
corpo del sottoprogramma {…}
double sqrt(double x) { // codice }
• Chiamate (devono essere precedute almeno
dalla dichiarazione):
sqdelta = sqrt(b*b-4*a*c);
Funzioni
x, y parametri formali
int max (int x, int y)
Contesto di
definizione
{
if (x > y) return x
else return y;
}
int main ()
{
int n = 7, m
= 14, k;
k = max(n, m);
Contesto di
chiamata
n, m parametri attuali
cout << "il massimo tra " << n
<< " e " << m << " e' "
<< k << endl;
}
Funzioni
main()
max()
n
7
7
x
7
m
14
14
y
14
k
14
14
Return_value
14
Funzioni: return
• Una funzione normalmente restituisce un
valore attraverso l’istruzione (usata nel
corpo del contesto di definizione)
return <espressione>;
double cube (double x)
{
return x*x*x;
}
il tipo dell’espressione deve coincidere con
quello del valore della funzione!!!
Funzioni e assegnamenti
• Il valore restituito da una funzione
può essere salvato assegnandolo ad
una variabile:
k = max(n, m);
il massimo tra i valori di n, ed m è ora
il nuovo R-value di k.
Funzioni e espressioni
• Una funzione di tipo T è
un’espressione di tipo T e può essere
usata all’interno di un’epressione:
7 + max(5, m*2);
Nel contesto di
chiamata un
parametro attuale può
essere un’espressione
Funzioni void
• Se una funzione non deve restituire
alcun valore (ad esempio una routine
di stampa) ha tipo void:
void Ciao ()
{
}
cout << "ciao! " << endl;
Errori frequenti
int f(int n)
{
cout << "dammi un numero: ";
cin >> n; // il valore passato ad f()
// viene così ignorato !!!
...
}
Errori frequenti
int f(int n)
{
...
return ...
}
int main()
{
int num;
cin >> num;
f(num); // il valore restituito da f() va perso
...
}
Variabili locali
• All’interno del corpo di una funzione
può essere utile avere variabili che
esistano solo durante l’esecuzione: si
dicono locali o automatiche:
int f(int n)
{
int r, s;
...
}
Ambito di visibilità
di r ed s (e di n)
Variabili locali
Le variabili locali di una funzione:
• sono visibili solo dalle istruzioni nel
corpo della funzione
• esistono solo finché la funzione è in
esecuzione, quindi spariscono (come
vedremo più avanti nel corso, vengono
deallocate dallo stack).
Lo Stack di sistema
• Per consentire l’uso delle variabili locali il
sistema gestisce uno stack (pila), cioè una
torta di ambienti, che associano alle
variabili un L-value
Lo Stack di sistema
double f(double z)
{
float x; …}
int main()
{
double x, y; … f(x); … }
Leggiamo dall’alto in basso,
fermandoci al primo blocco
che incontriamo
Le sole variabili visibili
durante l’esecuzione di
f() sono quelle nel
riquadro più in alto
x → 1200CF, z → 1205B1A
x → 06A231, y → 105BA3
Le variabili dei blocchi più in
basso non sono visibili
durante l’esecuzione di f(),
ma continuano ad esistere
Lo Stack di sistema
double f(double x)
{
float x; …}
La x più interna rende
invisibile il parametro x
int main()
{
double x, y; … f(x); … }
Leggiamo da sinistra a destra, fermandoci alla prima x che troviamo
x → 1200CF, x → 1205B1A
x → 06A231, y → 105BA3
Blocchi
• La dichiarazione delle variabili locali può
essere annidata in blocchi delimitati da { } :
int f(int n)
{
int s = 0;
{
int r, s = 1; // la seconda s fa ombra alla prima
... }
cout << r; // r non esiste più
cout << s; // s vale ancora 0
}
Errori frequenti
int f(int n)
{
int s; ...
s = ...
}
int main()
{
int m = f(7);
int p = s; // s non esiste più
...
}
Variabili globali
• Tutte le variabili sin qui viste sono locali:
anche main() è una funzione!
• Si possono dichiarare variabili visibili
ovunque (tranne in blocchi che abbiano
variabili locali omonime) e persistenti per
tutta l’esecuzione del programma: le
variabili globali.
#include <iostream>
#include <stdlib.h>
using namespace std;
double pi = 3,14159; // esisterà per tutto il progr.
Variabili globali (no grazie)
• Salvo quando il programma gestisca grandi
strutture dati permanenti, l’uso delle
variabili globali è sconsigliato (è un cattivo
stile di programmazione):
double k = 0.0; // esisterà per tutto il progr.
void f(double x) { k = sqrt(x); }
int main()
{
f(25);
cout << k; // stampa 5.0, ma non si capisce
// come mai k abbia assunto quel valore !!
}
Passaggio per riferimento
• Il passaggio dei parametri è per
valore (copia)
• Consideriamo però il codice che segue:
void scambia (int x, int y)
Dovrebbe scambiare i valori
dei parametri
{
}
int temp = x; x = y; y = temp;
int main()
{
int n = 45, m = 0; scambia(n, m);
cout << n << “\t” << m; // appare: 45
}
0
!!
Passaggio per riferimento
• In C++ si può passare l’indirizzo del
parametro attuale (ossia l’L-value di
una variabile), definendo il parametro
formale corrispondente come un
riferimento (uso di &):
void scambia (int & x, int & y)
{
int temp = x; x = y; y = temp;
}
Passaggio per riferimento
main()
n
45
m
0
scambia()
x
temp
45
y
x ed y in scambia() assumono lo stesso L-value di n,
m in main(), ossia ne condividono l’indirizzo.
Uso
• Il passaggio per riferimento può essere
utile quando si deve restituire più di un
valore:
void Div (int n, int m, int & q, int & r)
// calcola quoziente e resto di n div. m
{
q = 0;
r = n;
while (r > m)
{
r = r - m;
q++;
}
}
Uso
• Il passaggio per riferimento può essere
utile quando si deve restituire più di un
valore:
void Div (int n, int m, int & q, int & r)
// calcola quoziente e resto di n div. m
{ …
}
int main()
{
int resto, quoziente;
Div(20, 3, quoziente, resto);
cout << quoziente << "\t" << resto << endl;
}
Errori frequenti
• Il parametro attuale che corrisponde
ad un parametro formale passato per
riferimento deve avere un L-value
(per quanto ne sappiamo fin qui può
solo essere una variabile):
int main ()
{
... scambia(7, 5+9); // ERRORE; né 7 né 5+9
// hanno L-value
}
Che cosa calcola una
funzione?
Che cosa calcola una funzione?
La risposta è una specifica dell’algoritmo.
Vorrei calcolare
valori con la
proprietà ψ
Post-condizione
Posso farlo
purché i dati
soddisfino ϕ
Sig. Utente
Pre-condizione
Sig. Funzione
Pre e Post condizioni
• La pre-condizione è una richiesta,
ossia un’ipotesi sull’ingresso (dunque
non è necessario controllarla)
• La post-condizione è una proprietà
dell’uscita di una funzione, che si deve
garantire che valga dopo l’esecuzione.
Pre e Post condizioni
int MCD (int n, int m)
// pre: n, m positivi non entrambi nulli
// post: restituisce il massimo comun
// divisore tra n ed m
void Div (int n, int m, int & q, int & r)
// pre: m != 0
// post: q quoziente, r resto della div. n:m
Riepilogo
• I sottoprogrammi sono parti di un
programma la cui esecuzione può essere
ripetuta in punti diversi del programma
principale
• Le funzioni sono sottoprogrammmi con
parametri
• Le funzioni compaiono in un programmi in
contesti differenti: di dichiarazione, di
definizione e di chiamata
Riepilogo
• Le funzioni possono restituire un valore,
oppure no (allora sono di tipo void)
• I parametri sono passati per valore e per
riferimento
• Le variabili locali oscurano le variabili
globali ed i parametri
• Una funzione restituisce un valore, e può
essere usata in un’espressione al posto del
valore