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
© Copyright 2025 ExpyDoc