Scope delle variabili e passaggio parametri Danilo Ardagna Politecnico di Milano 1-4-2014
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 2
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 3
Esempi int x; f() { int y; y=1; int x; g(int y, char z) { int k; int l; 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à f(int x) { char x; Errata! Si tenta di definire due volte la variabile locale x nello stesso blocco 4
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; int f() { int x; x=1; { int x; x=2; x=3; Leggi(x); /*nome globale*/ /*x locale che nasconde x globale*/ /*assegna 1 al primo x locale*/ /* nasconde il primo x locale*/ /*assegna 2 al secondo x locale*/ /*assegna 3 al primo x locale*/ /*inserisce un dato in x globale*/! In caso di omonimia di identificatori in ambienti diversi è visibile quello dell ambiente più vicino 5
Visibilità char g1, g2, g3; main() { int a, b; {/*blocco1*/ double a,c void f1(){ {/*blocco2*/ char a,d; {/*blocco3*/ float d g1,g2,g3 a,b a,c a,d d Livello globale main blocco1 blocco2 blocco3 f1 6
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) 7
Ambiente di esecuzione: esempio #include <stdio.h> void a (); void b(); void c(); main(){ 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 ); 8
Ambiente di esecuzione: esempio Esecuzione di a Esecuzione di b Esecuzione di c Termine di c Termine di b Termine di a Ambiente c Ambiente b Ambiente a 9
Record di attivazione! Alla chiamata di una funzione! Si alloca uno spazio di memoria (record di attivazione) in cima allo stack per contenere l ambiente locale della funzione! Lo spazio viene rilasciato quando la funzione termina! Il record di attivazione contiene:! L ambiente locale della funzione (parametri formali e le variabili locali)! L indirizzo di ritorno al chiamante 10
Funzionamento del record di attivazione! 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() 11
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) 12
Lo stack SP 312 Operazione di inserimento: -incremento SP -scrittura nella parola indirizzata da SP Operazione di estrazione: -lettura dalla parola indirizzata da SP -decremento SP 312 311 310... 303 13
Esempio Main P1 P2 Chiama P1... etc. Act. Record P2(2) Act. Record P1(2) Act. Record P2(1) Act. Record P1(1) Act. Record Main Variabili globali 14
Ricerca in array ordinati int ricercaarray(int v[], int elemento, int n) { int i; int trovato; for (i=0,trovato=0;i<n &&!trovato&& v[i]<=elemento;){ if (v[i]==elemento) trovato=1; else i++; if (trovato) return i; else return -1; 15
Ricerca in array ordinati int ricercabinaria(int v[], int elemento, int n) { int i; int end=n-1;int start=0; while(end >= start) { i= (start + end)/2; if(elemento == v[i]) return i; if (elemento < v[i]) end=(i-1); else start=(i+1); return -1; 16
Esercizi! Scrivere un programma che, utilizzando opportune procedure (o funzioni, si scelga la forma più adatta) esegua le seguenti operazioni: 1. Legga da tastiera una matrice m di interi, di R righe e C colonne (R e C costanti) 2. Verifichi se, in ciascuna riga della matrice, c e almeno un valore pari 3. Stampi a video il risultato del controllo descritto al punto 2 17
Esercizi! Scrivere un programma che, utilizzando opportune procedure (o funzioni, si scelga la forma più adatta) esegua le seguenti operazioni: 1. Legga da tastiera un array a di N interi 2. Stampi a video il numero di valori pari presenti nell array 3. Cambi tutti i valori dispari presenti nell array con uno zero 4. Stampi a video il nuovo array 18
Funzioni: Scambio di informazioni! Il chiamante deve poter fornire i valori attuali (specifici) 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 (in entrambe le direzioni)! Per fornire i valori in ingresso al chiamato! Per fornire i risultati di operazioni al chiamante! Variabili globali (in entrambe le direzioni)! Fortemente sconsigliato 19
Passaggio dei parametri! Il passaggio dei parametri consiste nell associare, all atto della chiamata di un sottoprogramma, ai parametri formali i parametri attuali! Se il prototipo di una funzione è float circonferenza (float raggio);! Eseguiamo la funzione con 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 20
Passaggio per valore! All atto della chiamata il valore del parametro attuale viene copiato nelle celle di memoria del corrispondente parametro formale. In altre parole 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! Meccanismo per fornire informazioni a sola lettura alle funzione 21
Esempio: passaggio per valore float circonferenza(float raggio) { float circ; circ = raggio * 6.28; 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 raggio Quando invoco la funzione in raggio viene copiato il valore di r r 5 5 circ Ambiente della funzione main c 22
Esempio: passaggio per valore float circonferenza(float raggio) { float circ; circ = raggio * 6.28; 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 raggio Quando invoco la funzione in raggio viene copiato il valore di r r 5 57 circ 31.4 Quando la funzione termina il valore di circ in circonferenza viene copiato in c nel main Ambiente della funzione main c 31.4 23
Esempio: passaggio per valore float circonferenza(float raggio) { float circ; circ = raggio * 6.28; 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 */ r 5 c 31.4 Ambiente della funzione main 24
Esempio: scambio di 2 valori interi void swap (int p, int q) { int temp; temp = p; p = q; q = temp; Nel main: swap(a,b) a 3 b 7 25
Esempio: scambio di 2 valori interi void swap (int p, int q) { int temp; temp = p; p = q; q = temp; Nel main: swap(a,b) a 3 b 7 p 3 q 7 temp 3 26
Esempio: scambio di 2 valori interi void swap (int p, int q) { int temp; temp = p; p = q; q = temp; Nel main: swap(a,b) a 3 b 7 p 7 q 7 temp 3 27
Esempio: scambio di 2 valori interi void swap (int p, int q) { int temp; temp = p; p = q; q = temp; Nel main: swap(a,b) a 3 b 7 p 7 q 3 temp 3 28
Esempio: scambio di 2 valori interi void swap (int p, int q) { int temp; temp = p; p = q; q = temp; Nel main: swap(a,b) Al termine dell esecuzione di swap le variabili nel main restano inalterate! a 3 b 7 p 7 q 3 temp 3 29
Passaggio per indirizzo! All atto della chiamata l indirizzo dei parametri attuali viene associato ai parametri formali. In altre parole il parametro attuale e il parametro formale si riferiscono alla stessa cella di memoria! Il sottoprogramma in esecuzione lavora nel suo ambiente sui parametri formali (e di conseguenza anche sui parametri attuali) e ogni modifica sul parametro formale è una modifica del corrispondente parametro attuale! Gli effetti del sottoprogramma si manifestano nel chiamante con modifiche al suo ambiente locale di esecuzione! Meccanismo per implementare uno scambio di informazioni bidirezionale con le funzioni 30
Passaggio per indirizzo in C! Richiede l uso dei puntatori. Aspetti che non vedremo in questo corso! Ci limiteremo a dire che:! I parametri il cui tipo è un tipo base o struct vengono passati per valore! I parametri il cui tipo è array sono sempre passati per indirizzo e pertanto se una funzione modifica un parametro formale ti tipo array allora le modifiche saranno visibili anche esternamente 31
1 2 4 #include <stdio.h> 5 #define SIZE 10 6 void bubblesort( int array[], int size); 7 8 void main() 9 { 10 11 int a[ SIZE ] = { 2, 6, 4, 8, 10, 12, 89, 68, 45, 37 ; 12 int i; 13 14 printf( "Data items in original order\n" ); 15 16 for ( i = 0; i < SIZE; i++ ) 17 printf( "%4d", a[ i ] ); 18 19 bubblesort( a, SIZE ); /* ordina */ 20 printf( "\ndata items in ascending order\n" ); 21 22 for ( i = 0; i < SIZE; i++ ) 23 printf( "%4d", a[ i ] ); 24 25 printf( "\n" ); 26 27 28 29 Data items in original order 2 6 4 8 10 12 89 68 45 37 Data items in ascending order 2 4 6 8 10 12 37 45 68 89
30 void bubblesort( int array[], int size ) 31 { 32 33 int i, j, swap; 34 for ( i= 0; i< size - 1; i++ ) 35 36 for ( j = 1; j < size; j++ ) 37 38 if ( array[ j ] < array[ j - 1 ] ){ 39 swap = array[j-1]; 40 array[j-1]=array[j]; 41 array[j]=swap; 40 42 43 http://www.youtube.com/watch?feature=endscreen&nr=1&v=mtcrehrt_k0 http://www.youtube.com/watch?v=p00xjgwzz2c http://en.wikipedia.org/wiki/file:bubble_sort_animation.gif
Commento! Esistono moltissimi algoritmi di ordinamento! Quello visto si chiama bubblesort! Differiscono per l'efficienza (tempo di esecuzione)! Sono ampiamente studiati come esempio per l'analisi degli algoritmi 34
Altri algoritmi! Shaker sort: come il bubble sort ma anzichè ripartire dall inizio per fare i confronti si alternano il verso di scans scansione dell array ripartendo dal fondo (come in un ascensore) http://www.youtube.com/watch?v=74u4mszivbu 35