370 likes | 697 Views
Funzioni e Procedure. Marco D. Santambrogio – marco.santambrogio@polimi.it Ver. aggiornata al 18 Aprile 2012. Obiettivi. Funzioni Scope delle variabili. Introduzione. Spesso alcuni gruppi di operazioni vengono ripetute in diverse parti all ’ interno del medesimo programma
E N D
Funzioni e Procedure Marco D. Santambrogio – marco.santambrogio@polimi.it Ver. aggiornata al 18 Aprile 2012
Obiettivi • Funzioni • Scope dellevariabili
Introduzione • Spesso alcuni gruppi di operazioni vengono ripetute in diverse parti all’interno del medesimo programma • Si pensi ad esempio a un programma di gestione di matrici • È plausibile pensare che una matrice dovrà essere stampata a video in numerosi momenti • Il codice relativo alla stampa è sempre lo stesso (due cicli for annidati) • È quindi utile definire una sola volta questo gruppo di istruzioni e dare un nome ad esso • Ogni volta che si vuole stampare a video una matrice sarà sufficiente indicare il nome assegnato
Sottoprogrammi • Un sottoprogramma è: • un insieme di istruzioni dotato di nome • descritto (definito) una sola volta • attivabile (richiamabile o invocabile) all’interno del programma o di un altro sottoprogramma • Alcuni sottoprogrammi sono già definiti • si pensi alla scanf e alla printf • dietro a questi nomi vi sono una serie di istruzioni in grado di, rispettivamente, intercettare la pressione dei tasti e di visualizzare un carattere sullo schermo • chi richiama queste funzioni non si preoccupa di come sono fatte, basta sapere solo cosa fanno (visione black box)
Sottoprogrammi: motivazioni • Astrazione e leggibilità: • enucleano parti di codice, nascondendo dettagli algoritmici e di codifica • il nome di programma si presenta come “un’operazione elementare” • Strutturazione e scomposizione funzionale del programma: • consentono una stesura del programma che riflette un’analisi funzionale del problema • Collaudo: • verifica di correttezza della soluzione facilitata dal poter verificare la correttezza prima dei singoli sottoprogrammi e poi dell’intero programma visto come insieme di chiamate che si scambiano informazioni • Compattezza ed efficienza del codice: • si evita di ripetere sequenze di istruzioni in più parti del programma • Modificabilità: • una sola modifica vale per tutte le attivazioni del sottoprogramma • Riuso: • sottoprogrammi non troppo specifici possono essere raccolti in librerie utilizzabili da programmi diversi
Funzioni e procedure • I sottoprogrammi si differenziano per la logica di definizione per l’uso e per la modalità di chiamata e possono essere • di tipo funzionale • di tipo procedurale • Sottoprogrammi di tipo funzionale (funzioni) possono essere considerati una astrazione di valore • l’invocazione della funzione associa al nome della funzione il valore del risultato calcolato dal sottoprogramma • Sottoprogrammi di tipo procedurale (procedure) possono essere considerati una astrazione di operazioni • l’invocazione della procedura è associata all’esecuzione delle istruzioni del sottoprogramma che realizzano l’operazione specificata dal sottoprogramma • Ad esempio: leggi(N); /* procedura*/ Fatt = fattoriale(N); /* funzione*/
Funzioni e procedure in C • In C esistono solo le funzioni. • Le procedure sono particolari funzioni che non restituiscono nulla • Quindi parleremo solo di funzioni intendendo sia le funzioni che le procedure • Definire una funzione secondo il linguaggio C implica: • dichiarazione del prototipo della funzione (nella sezione dichiarativa) • definizione della funzione • invocazione o chiamata della funzione (nel codice che necessita della funzioni)
Calcolare la somma dei fattoriali dei primi 100 numeri naturali: 0!+1!+2! … + 99!+100! #include <stdio.h> int fattoriale(int); main(){ int numero; /* numero di cui voglio calcolare il fattoriale */ int fatt; /* memorizzo il fattoriale di numero */ int somma; somma = 0; for(numero=0; numero<=100; numero++) { fatt = fattoriale(numero); somma = somma + fatt; } printf(“La somma dei fattoriali dei primi 100 naturali e’ %d”, somma);
Dichiarazione del prototipo • Il prototipo definisce: • il nome della funzione • il tipo (funzione, procedura) • il tipo dei parametri in ingresso e in uscita • Chi utilizzerà la funzione dovrà rispettare la sintassi definita nel prototipo • Prototipo funzione <tipo_ris> <nome_funz> (<lista tipi dei parametri>); • Prototipo procedura (void è una parola chiave del C che indica assenza di tipo) void <nome_funz> (<lista tipi dei parametri>);
Definizione del sottoprogramma • La definizione del sottoprogramma va messa dopo il main. Ha la stessa struttura del main (in realtà anche il main è una funzione): • Una parte dichiarativa • Una parte esecutiva <tipo> <nome> (tipo par_for1, tipo par_for2 ...) { parte dichiarativa locale parte esecutiva } • <tipo> <nome> (tipo par_for1, tipo par_for2 ...) è la testata della funzione • par_for1, par_for2 sono i nomi dei parametri formali della funzione, il cui tipo deve corrispondere in modo ordinato ai tipi elencati nella dichiarazione del prototipo
Definizione della funzione fattoriale e i parametri formali int fattoriale(int n){ int risultato=1; int i; for (i = 1; i <= n; i++) risultato = risultato * i; return risultato; } • Parametri Formali: • Rappresentano un riferimento simbolico (identificatori) a oggetti utilizzati all’interno della funzione • Sono utilizzati dalla funzione come se fossero variabili dichiarate localmente • Il valore iniziale di parametri formali viene definito all’atto della chiamata della funzione tramite i parametri attuali (passaggio di parametri) Le funzioni in C sono funzioni in senso matematico, il tipo del valore di ritorno definisce il Codominio mentre i valori possibili dei parametri in ingresso corrispondono al Dominio
Invocazione o chiamata • Nel corpo del main o di un’altra funzione indica il punto in cui va eseguita la parte del codice presente nella definizione di funzione • Invocazione di funzione • come operando in una espressione • <espressione> = <… nomefunzione(par_att1 …)…>; • Invocazione di procedura • come un’istruzione • nomeprocedura(par_att1, …); • In entrambi i casi: • par_att1,… sono i parametri attuali che devono corrispondere per ordine e per tipo ai parametri formali • I parametri attuali possono essere variabili, costanti o espressioni definite nell’ambiente chiamante, i cui valori all’atto della chiamata vengono copiati nei parametri formali
Istruzione return • Parola chiave C utilizzata solo nelle funzioni • Sintassi return <espressione> • È l’ultima istruzione di una funzione e indica: • il punto in cui il controllo torna al chiamante • il valore restituito • In una funzione • deve esserci almeno un’istruzione di return • possono esserci più istruzioni di return ma in alternativa • la funzione può restituire un solo valore
Chiamata di un sottoprogramma • All’atto della chiamata, il controllo dell’esecuzione passa dal chiamante al chiamato • Il codice del chiamato viene eseguito • Al termine dell’esecuzione il controllo ritorna al chiamante, all’istruzione successiva a quella della chiamata codice chiamante Inizio programma Istruzione 1 Istruzione 2 Chiama funzione Istruzione 3 Passaggio del controllo Ritorno del controllo funzione chiamata Istruzione 1 Istruzione 2 Istruzione 3 Istruzione 4 return
Scambio di informazioni • Il chiamato deve poter ricevere dal chiamante i valori attuali sui quali eseguire le operazioni definite nel sottoprogramma • Il chiamato deve poter fornire al chiamante i risultati dell’elaborazione • Lo scambio di informazioni tra chiamante e chiamato può avvenire: • valore restituito da una funzione (dal chiamato al chiamante) • vale solo per le funzioni • per fornire un valore al chiamante • passaggio dei parametri (dal chiamante al chiamato) • per fornire i valori in ingresso al chiamato • per fornire i risultati di operazioni al chiamante • variabili globali (in entrambe le direzioni) • fortemente sconsigliato
Passaggio dei parametri • Il passaggio dei parametri consiste nell’associare, all’atto delle chiamata di un sottoprogramma, ai parametri formali i parametri attuali • Se il prototipo di una funzione è float circonferenza (float raggio); • Invocare questa funzione significa eseguire l’istruzione c = circonferenza(5.0); • In questo modo la variabile raggio (il parametro formale) assumerà per quella particolare invocazione il valore 5 (il parametro attuale). • Lo scambio di informazioni con passaggio dei parametri tra chiamante e chiamato può avvenire in due modi: • Passaggio per valore • Passaggio per indirizzo
Passaggio per VALORE • All’atto della chiamata il valore del parametro attuale viene copiato nelle celle di memoria del corrispondente parametro formale. • Il parametro formale e il parametro attuale si riferiscono a due diverse celle di memoria • Il sottoprogramma in esecuzione lavora nel suo ambiente e quindi sui parametri formali • I parametri attuali non vengono modificati
Esempio: passaggio per valore float circonferenza(float raggio) { float circ; circ = raggio * 3.14; raggio = 7; /*istruzione senza senso, voglio solo vedere cosa succede modificando il valore di un paramentro formale*/ return circ; } /* nel main */ float c,r=5; c=circonferenza(r); /*Attenzione! r vale sempre 5 */ Ambiente della funzione circonferenza circ raggio Quando la funzione termina il valore di circ in circonferenza viene copiato in c nel main Quando invoco la funzione in raggio viene copiato il valore di r c r Ambiente della funzione main
Introduzione • Con le funzioni è stato introdotto un meccanismo per definire dei piccoli programmi all’interno di altri programmi • Questi piccoli programmi sono autonomi con il resto del codice • Al loro interno sono definite le proprie variabili ed il canale di comunicazione con il codice chiamante (passaggio paramentri, return) • A supporto di questo meccanismo deve esistere una gestione degli identificatori delle variabili • È infatti possibile avere due variabili con lo stesso nome a patto che abbiano raggio di visibilità disgiunto
Visibilità • Visibilità di un identificatore: • indicazione della parte del programma in cui tale identificatore può essere usato • Ambiente globale del programma • insieme di identificatori (tipi, costanti, variabili) definiti nella parte dichiarativa globale • regole di visibilità: visibili a tutte le funzioni del programma • Ambiente locale di una funzione • insieme di identificatori definiti nella parte dichiarativa locale e degli identificatori definiti nella testata (parametri formali) • Regole di visibilità: visibili alla funzione e ai blocchi in essa contenuti • Ambiente di blocco • insieme di identificatori definiti nella parte dichiarativa locale del blocco • regole di visibilità: visibili al blocco e ai blocchi in esso contenuti
Esempi int x; f() { int y; y=1; } int x; g(int y, char z) { int k; int l; … } f(int x) { int x; } • La visibilità di y si estende dal punto di dichiarazione fino alla fine del blocco di appartenenza • y e z locali alla funzione g,con visibilità nel blocco racchiuso da parentesi graffe • k e l hanno la stessa visibilità • Errata! Si tenta di definire due volte la variabile locale x nello stesso blocco
Mascheramento (shadowing) • Un nome ridefinito all’interno di un blocco nasconde il significato precedente di quel nome • Tale significato è ripristinato all’uscita del blocco più interno int x; /*nome globale*/ int f() { int x; /*x locale che nasconde x globale*/ x=1; /*assegna 1 al primo x locale*/ { int x; /* nasconde il primo x locale*/ x=2; /*assegna 2 al secondo x locale*/ } x=3; /*assegna 3 al primo x locale*/ } scanf (“%d”, &x); /*inserisce un dato in x globale*/ • In caso di omonimia di identificatori in ambienti diversi è visibile quello dell’ambiente più “vicino”
Visibilità Livello globale g1,g2,g3 a,b main a,c blocco1 f1 a,d blocco2 d blocco3 char g1, g2, g3; main() { int a, b; … {/*blcco1*/ double a,c; } … } void f1(){ … {/*blocco2*/ char a,d; } … {/*blocco3*/ float d … } }
Ambiente di esecuzione • L’ambiente di esecuzione di una funzione (variabili e parametri formali) viene creato al momento della chiamata e rilasciato quando la funzione termina • In una sequenza di chiamate, l’ultima chiamata è la prima a terminare • La zona di memoria di lavoro che contiene l’ambiente di esecuzione di un sottoprogramma è gestito con la logica di una pila (stack) • L’ultimo elemento inserito nello stack è il primo ad essere estratto • Logica LIFO (Last In First Out)
Ambiente di esecuzione: esempio Ambiente c Ambiente b Ambiente a void a (); void b(); void c(); main(){ … a(); … } Esecuzione di a Esecuzione di b Esecuzione di c Termine di c Termine di b Termine di a void a (){ printf(“Esecuzione di a\n”); b(); printf(“Termine di a\n”); } void b(){ printf(“Esecuzione di b\n”); c(); printf(“Termine di b\n”); } void c(){ printf(“Esecuzione di c\n”); … printf(“Termine di c\n”); }
Record di attivazione • Alla chiamata di una funzione • si alloca uno spazio di memoria (record di attivazione) in cima allo stack per contenere i parametri formali e le variabili locali • lo spazio viene rilasciato quando la funzione termina • Il record di attivazione contiene: • L’ambiente locale della funzione • L’indirizzo di ritorno al chiamante • Funzionamento: • Ad ogni attivazione viene allocato un record di attivazione • Al termine dell’attivazione il record viene rilasciato (l’area di memoria è riutilizzabile) • La dimensione del record di attivazione è già nota in fase di compilazione • Il numero di attivazioni della funzione non è noto • Il primo record di attivazione è destinato al main()
Lo stack • Nello stack, i record vengono allocati “uno sopra l’altro”; il primo record dello stack è relativo all’ultima funzione attivata e non ancora terminata • Lo stack cresce dal basso verso l’alto • Stack pointer: registro della CPU che contiene l’indirizzo della cima della pila • Una parte della RAM è destinata a contenere lo stack • Stack overflow: quando l’area della RAM destinata allo stack viene superata (troppi annidamenti di chiamate) SP 312 312 311 Operazione di inserimento: -incremento SP -scrittura in parola indirizzata da SP Operazione di estrazione: -lettura da parola indirizzata da SP -decremento SP 310 ... 303
Esempio ... etc. Act. RecordP2(2) Act. RecordP1(2) Act. RecordP2(1) Act. RecordP1(1) Act. RecordMain Variabili globali Main P1 P2 Chiama P1
Fonti per lo studio + Credits • Fonti per lo studio • Credits • Gianluca Palermo