OBIETTIVO DELLA LEZIONE
Introduzione all'uso di funzioni e di prototipi
- Capire perchè è importante progettare il codice mediante funzioni e prototipi
- Capire cosa sia un prototipo di una funzione e cosa serve al compilatore
Passaggio parametri- Capire il funzionamento del passaggio parametri, per valore e per indirizzo, alle funzioni
- Capire come si passano alle funzioni i vettori, le matrici e tipi di dato strutturato
- Essere in grado di passare correttamente i parametri alle funzioni
Ricorsione- Comprendere l'uso della ricorsione come strumento di programmazione
- Identificare i punti di forza e di debolezza della ricorsione
- Essere in grado di impiegare la ricorsione correttamente nei propri programmi
Modalità di riuso del codice in una funzione- Capire le modalità per incapsulare codice già scritto in altre funzione
- Essere capaci di riusare codice già scritto nelle funzioni
// SWAP #include <stdio.h> #include <stdlib.h> void swap1( int local_a , int local_b ); void swap2( int a , int b ); int main() { int a; int b; a = 3; b = 5; printf("a vale %d e b vale %d \n" , a , b); swap1( a, b); printf("a vale %d e b vale %d \n" , a , b); swap2( a, b); printf("a vale %d e b vale %d \n" , a , b); getchar(); getchar(); exit(0); } void swap1( int local_a , int local_b ) { int temp ; temp = local_a ; local_a = local_b ; local_b = temp ; } void swap2( int a , int b ) { int temp ; temp = a ; a = b ; b = temp ; }
tutto quello che si ottiene e' semplicemente
a vale 3 e b vale 5 a vale 3 e b vale 5 a vale 3 e b vale 5
// SWAP #include <stdio.h> #include <stdlib.h> void swap_OK( int * i_a , int * i_b ); int main() { int a; int b; a = 3; b = 5; printf("a vale %d e b vale %d \n" , a , b); swap_OK( &a, &b); printf("a vale %d e b vale %d \n" , a , b); getchar(); getchar(); exit(0); } void swap_OK( int * i_a , int * i_b ) { int temp ; temp = *(i_a) ; *(i_a) = *(i_b) ; *(i_b) = temp ; }
Ora funziona correttamente ed inverte il contenuto delle due celle a e b
a vale 3 e b vale 5 a vale 5 e b vale 3
printf("%.3f \n" , sqrt(4) );
2.000
Visualizza 2,000 (float) e non 2 intero (intero) la coercizione
converte 4 (int) in 4 (float) prima di passarlo veramente a sqrt()
Per restituire un risultato di una funzione si puo' utilizzare il return
#include <stdio.h> #include <stdlib.h> int square( int y ) ; // prototipo int main() { int b,a=3; b=square(a); // chiamata printf( "Il quadrato di %d e' %d", a, b); fflush(stdin); getchar(); return(0); } int square( int y ) // definizione { return ( y * y ); // restituzione del risultato }
Il quadrato di 3 e' 9
Per restituire un risultato di una funzione si puo' direttamente passare alla funzione l'indirizzo della variabile in cui va messo il risultato.
#include <stdio.h> #include <stdlib.h> void square( int y, int *x ) ; // prototipo int main() { int b,a=3; square(a,&b); // chiamata printf( "Il quadrato di %d e' %d", a, b); fflush(stdin); getchar(); return(0); } void square( int y, int *x ) // definizione { *x= ( y * y ); // il risultato viene inserito nella cella di memoria puntata da x }
Il quadrato di 3 e' 9
Il risultato e' identico: b contiene il quadrato di a. Questa seconda versione e' utile quando i risultati di una funzione sono piu' di uno (si puo' restituire un solo risultato con il return).
Rileggi velocemente il riassunto della lezione di stamattina che trovi in questa pagina (sezione Note). I costrutti presentati saranno molto utili per risolvere gli esercizi di oggi.
Esercizio 1 (Ripasso teoria funzioni)
Prova a rispondere alle seguenti domande.
Se non trovi le risposte o non sei sicuro, rivedi la lezione.
-------------------------------------------------------------------------------- 1. Il prototipo di una funzione è: - uno strumento che il compilatore usa per definire i tipi di dato in ingresso e uscita alla funzione - una funzione solo abbozzata, appunto allo stato di prototipo - una nota per gli sviluppatori futuri -------------------------------------------------------------------------------- 2. Il prototipo di una funzione finisce sempre con un ";". - Vero - Falso, finisce con una graffa aperta a cui segue il codice della funzione -------------------------------------------------------------------------------- 3. La definizione di una funzione ha come prima riga il prototipo della funzione stessa. - Falso, il prototipo può non contenere il nome dei parametri formali, quindi non sempre sono uguali - Vero, sono sempre uguali -------------------------------------------------------------------------------- 4. Nel prototipo i parametri formali ... - si inseriscono solo a scopo documentativo, il compilatore li ignora e guarda solo il TIPO dei dati passati - devono sempre essere inclusi altrimenti il compilatore non riesce ad usarlo -------------------------------------------------------------------------------- 5. I file che includiamo con la direttiva #include possono essere delle semplici collezioni di prototipi di funzioni. - Vero, il codice delle funzioni potrebbe essere già compilato in una libreria - Falso, se alla fine vogliamo compilare il nostro codice che usa quelle funzioni allora nel file incluso deve esserci TUTTO il codice -------------------------------------------------------------------------------- 6. Le variabili che definiamo in una funzione sono visibili all'esterno e quindi dobbiamo stare attenti con i nomi delle variabili perchè non si confondano con quelli del main. - Falso, proprio non si vedono e quindi non esiste questo problema - Vero, meglio usare sempre nomi diversi -------------------------------------------------------------------------------- 7. Usare delle variabili globali è sempre una buona idea che risolve spesso problemi di strutturazione del codice - Falso, è sinonimo di cattiva progettazione del codice, generano errori imprevisti e difficili da debuggare, sono da evitare come la peste. - Vero, è sempre consigliabile definire molte variabili globali. Infatti avendo il nome TUTTO IN MAIUSCOLO non possono essere confuse con le altre
Esercizio 2: passaggio parametri e discussione del tipo di dato + intervallo di rappresentazione per un int
Segui quanto di seguito elencato:
- Salva il codice sotto riportato in un programma fattorialeFunzionale1.c.
- Controlla il funzionamento del programma con una calcolatrice scientifica per i numeri 0, 1, 5, 10, 15, 20, 30, 100. Per quali numeri il programma funziona correttamente?
- Cambia il tipo di dato della variabile in uscita alla funzione per estendere l'intervallo di rappresentazione della variabile stessa: il programma elaborerà correttamente numeri fattoriali più grandi.
- Verifica fino a che numero fattoriale il tuo programma ora funziona correttamente.
// fattorialeFunzionale1.c #include <stdio.h> int calcola_fattoriale(int n); void main() { int n, fatt; // con delle virgole si dichiarano tante variabili int /* input dell'intero */ printf("intero---->"); scanf("%d", &n); /* calcolo del fattoriale */ fatt=calcola_fattoriale(n); /* visualizzazione risultato */ printf("\n Il fattoriale di %d e' l'intero %d\n",n,fatt); getchar(); } int calcola_fattoriale(int n) // Il calcola del fattoriale è racchiuso in questa funzione { int i, fatt=1; for (i=1; i<=n; i++) { fatt = fatt * i; } return(fatt); // ritorna il valore calcolato }
soluzione.
Esercizio 3: Scrittura intestazioni di funzioni
NB: per una agevole esecuzione dell'esercizio occorre avere chiaramente compreso la differenza fra PROTOTIPO e INTESTAZIONE.
Prova a scrivere su carta le INTESTAZIONI per le seguenti funzioni:
- funzione ipotenusa riceve due argomenti in virgola mobile in doppia precisione (lato1 e lato2 ) e ritorna un risultato in doppia precisione;
- funzione minoreditre che riceve tre argomenti interi ( a, b, c) e ritorna il valore del minore;
- funzione istruzioni che non ha parametri in ingresso e non ha parametri in uscita (NB: se ti stai chiedendo cosa serve, prova a pensare ad una funzione in un programma denominata istruzioni(); e che stampa a video un testo prefissato);
- funzione converti_Float2Intero che riceve un argomento Numero_Float e ritorna in uscita il valore intero più vicino.
soluzione.
Esercizio 4: Passaggio per indirizzo
Completa il programma concatena2stringhe.c sotto riportato, sviluppando le specifiche di codice/attività elencate:
- inserisci il prototipo e la definizione della funzione che avendo in ingresso due stringhe, le stampa a monitor concatenate (ossia sono stampate in sequenza senza spazi e senza andare a capo)
- salva il programma con il nome concatena2stringhe.c
- inserisci due stringhe e controlla se il codice è corretto
// concatena2stringhe.c #include <stdio.h> #include <stdlib.h> // inserisci qui il prototipo int main() { char stringa1[128]; char stringa2[128]; printf("Immettere la prima stringa\n"); scanf("%s",stringa1); printf("Immettere la seconda stringa\n"); scanf("%s",stringa2); stampa2Stringhe( stringa1 , stringa2 ); getchar(); exit(0); } // inserisci qui la definizione della funzione
void stampa2Stringhe ( char * stringa1 , char * stringa2 );Per quanto riguarda la definizione della funzione scrivi il codice che stampa la prima stringa carattere per carattere e aggiungi il codice che stampa la seconda stringa, ugualmente carattere per carattere. Oppure stampa le due stringhe una di seguito all'altra con una semplice printf.
Esercizio 5: Merge di due stringhe
Completa il programma concatena2stringhe_bis.c sotto riportato, sviluppando le specifiche di codice/attività elencate:
- inserisci il prototipo e la definizione di una funzione unisci2stringhe () che avendo in ingresso due stringhe e l'indirizzo di una terza, concatena le prime due nella terza. Ad esempio se viene "bella" e "giornata" nella terza stringa troveremo "bellagiornata"
- inserisci due stringhe: controlla la correttezza del codice e il risultato stampato
// concatena2stringhe_bis.c #include <stdio.h> #include <stdlib.h> #include <string.h> // inserisci qui il prototipo int main() { char stringa1[128]; char stringa2[128]; char stringaUnita[256]; // il carattere '\n' semplicemente manda a capo printf("Immettere la prima stringa\n"); scanf("%s",stringa1); printf("Immettere la seconda stringa\n"); scanf("%s",stringa2); unisci2stringhe ( stringa1 , stringa2 , stringaUnita ); printf(" %s + %s = %s ",stringa1, stringa2, stringaUnita ); getchar(); exit(0); }
soluzione.
Esercizio 6: Correggi il programma (passaggio parametri)
Dato il programma massimoFunzionale1.c sotto riportato, correggere gli errori, salvare il programma e compilarlo.
// massimoFunzionale1.c // ESERCIZIO: controllare il codice: sono presenti due errori // uno e' algoritmico, l'altro riguarda la composizione della FUNZIONE #include <stdio.h> #include <stdlib.h> #define LUN 3 int main() { int num; // dichiarazione delle variabili int i; int massimo; int numeri[LUN]; int calcolaMassimo(int numeri[]); // dichiarazione della funzione // solita immissione di un array for (i = 0; i < LUN; i = i + 1) { scanf("%d", &num); numeri[i] = num; } massimo = calcolaMassimo(numeri); // perche' non funziona? cosa manca nella funzione ?? printf("massimo=%d\n", massimo); getchar(); // non serve a nulla ma ferma la finestra... exit(0); } int calcolaMassimo(int numeri[]) { int massimo; int i; // vedo solo le variabili che vengono passate //e quelle dichiarate // quindi vedo e uso "massimo" e "i" // e l'indirizzo di dove inizia "numeri" // calcolo del massimo (SBAGLIATO da CORREGGERE) massimo = numeri[0]; for (i = 0; i < LUN; i = i + 1) { if (massimo == numeri[i]) { massimo = numeri[i]; } } }
soluzione.
Esercizio 7: Studio degli indirizzi dei parametri passati
Completa il programma massimoFunzionale3.c sotto riportato sviluppando le specifiche di codice/attività elencate:
- copia il programma massimoFunzionale3.c nella directory di lavoro;
- inserisci delle printf nel // punto A del codice che stampano a monitor le seguenti variabili: numeri massimo &massimo
- inserisci delle printf nel // punto B del codice che stampano a monitor le seguenti variabili: numeri DammiIndirizzoDelMassimo *DammiIndirizzoDelMassimo
- esamina i valori degli indirizzi di memoria che hai stampato a consolle e prova a fare uno schema su carta della memoria;
- stampa l'indirizzo &DammiIndirizzoDelMassimo e verifica la coerenza con lo schema della memoria che hai disegnato su carta;
- inserisci una printf nel // punto B del codice che visualizza il valore della variabile massimo. Che valore viene visualizzato?
// massimoFunzionale3.c // ESERCIZIO: passaggio parametri per indirizzo // Una procedura puo' cambiare delle celle FUORI da essa // se gli si passa l'INDIRIZZO DELLA VARIABILE #include <stdio.h> #include <stdlib.h> #define LUN 3 int main() { int num; // dichiarazione delle variabili int i; int massimo; int numeri[LUN]; void calcolaMassimo(int numeri[], int * DammiIndirizzoDelMassimo); // Questa e' una PROCEDURA !!! // solita immissione di un array for (i = 0; i < LUN; i = i + 1) { scanf("%d", &num); numeri[i] = num; } // punto A calcolaMassimo(numeri, & massimo); printf("massimo=%d\n", massimo); getchar(); // non serve a nulla ma ferma la finestra... exit(0); } void calcolaMassimo(int numeri[], int * DammiIndirizzoDelMassimo) { int massimoLocale; int i; // vedo solo le variabili che vengono passate //e quelle dichiarate // quindi vedo e uso "massimoLocale" e "i" // l'indirizzo dove inizia l'array "numeri" passato // // punto B massimoLocale = numeri[0]; for (i = 0; i < LUN; i = i + 1) { if (massimoLocale < numeri[i]) { massimoLocale = numeri[i]; } } *DammiIndirizzoDelMassimo= massimoLocale; // il valore della variabile LOCALE "massimoLocale" (che andrebbe persa // due righe sotto, alla chiusura della procedura) viene // copiato nella cella di indirizzo "DammiIndirizzoDelMassimo". // "DammiIndirizzoDelMassimo" contiene infatti l'indirizzo (esterno // alla memoria che puo' vedere la procedura) della variabile massimo // infatti chiamammo nel main: "calcolaMassimo(numeri, &massimo);" }
soluzione.
Esercizio 8: Passare le matrici alle funzioni (solo per indirizzo)
Scrivi un programma che crea e stampa la seguente matrice:Suggerimento
2 3 4 5 6 7 8 9 10 3 4 5 6 7 8 9 10 11 4 5 6 7 8 9 10 11 12 5 6 7 8 9 10 11 12 13 6 7 8 9 10 11 12 13 14 7 8 9 10 11 12 13 14 15 8 9 10 11 12 13 14 15 16 9 10 11 12 13 14 15 16 17 10 11 12 13 14 15 16 17 18Per sviluppare le attività richieste completa il programma gestioneMatrice.c sottoriportato:// gestioneMatrice.c #include <stdio.h> #include <stdlib.h> #define N_MAX 9 void riempi_Matrice(int a[][N_MAX]); void stampa_Matrice(int a[][N_MAX]); int main() { int a[N_MAX][N_MAX]; riempi_Matrice( a ); stampa_Matrice( a ); getchar(); exit(0); } // aggiungi le definizioni di funzioni che mancano
Esercizio 9: Passare i dati strutturati alle funzioni
Completa il programma gestioneMissItalia.c sotto riportato e scrivi la definizione della funzione aggiusta_voto().
//gestioneMissItalia.c #include <stdio.h> #include <stdlib.h> typedef struct { char nome[256] ; char cognome[256] ; int voto[3]; // voto[0] e' il voto della giuria (da 0 a 100), // voto[1] quello della giuria speciale e voto[2] il voto // telefonico. int altezza; int peso; int cucina; // 0= aiuto!!! 10= apre un ristorante } MissItalia; // Iniziale maiuscola void inserisci_miss( MissItalia * p1 ); // stampa i dati void aggiusta_voto( MissItalia * p1 ); // rende il voto perfetto (100, 100, 100)!! void stampa_miss( MissItalia * p1 ); // stampa i dati int main() { MissItalia ragazza1; MissItalia * p1; MissItalia ragazza2; MissItalia * p2; // sistemo i puntatori p1 = &ragazza1 ; p2 = &ragazza2 ; inserisci_miss ( p1 ); // i dati finiscono in ragazza1 inserisci_miss ( p2 ); // i dati finiscono in ragazza2 stampa_miss(p1); stampa_miss(p2); fflush(stdin); getchar(); exit(0); } void inserisci_miss ( MissItalia * p1 ) { char nome[256]; printf("Dammi il nome -->"); scanf("%s", p1->nome ); printf("Dammi il cognome -->"); scanf("%s", p1->cognome ); p1->altezza = 180; p1->voto[0] = 80; p1->voto[1] = 90; p1->voto[2] = 99; p1->peso = 78; p1->cucina = 0; // aiuto!!! } void stampa_miss ( MissItalia * p1 ) { printf("\n\n\n------------****------------\n"); printf("Nome:%s Cognome:%s \n", p1->nome , p1->cognome ); printf("altezza:%d Peso:%d \n", p1->altezza, p1->peso ); printf("voto: %d %d %d \n", p1->voto[0] , p1->voto[1] , p1->voto[2] ); printf("cucina [0=aiuto .. 10=professionista] = %d \n", p1->cucina ); }
soluzione.
Esercizio 10: RICORSIONE
Completare il programma Fibonacci_ricorsivo.c sotto riportato implementando la funzione Fib ().
La funzione Fib () riceve in ingresso un valore intero positivo n e restituisce un valore intero positivo pari a F(n), dove F(n) è tale per cui dato un valore n >1, F(n) = F(n-1) + F(n-2). Vale inoltre F(1) = 1 e F(0) = 0.
// Fibonacci_ricorsivo.c #include <stdio.h> #include <stdlib.h> // aggiungi prototipo int main () { int numero; printf("Immetti un numero maggiore o uguale a 0: "); scanf("%d",&numero); printf("F(%d) = %d\n",numero, Fib(numero) ); getchar(); return 0; } // inserisci qui la funzione ricorsiva // Ovviamente devi realizzare il prototipo e la definizione della funzione Fib
soluzione.
Esercizio 11: RIUSO DEL CODICE e incapsulamento
Modifica il programma massimoFunzionale1.c sotto riportato affinchè sia possibile chiamare nel main la funzione calcolaMassimo () di seguito definita:
.... int massimo; int numeri[LUN]; ..... massimo = calcolaMassimo(numeri);
La traccia del codice:// massimoFunzionale1.c // ESERCIZIO: chiudere la parte necessaria del codice che calcola il massimo // in una funzione calcolaMassimo(...) ed chiamarla nel main #include <stdio.h> #include <stdlib.h> #define LUN 3 int main() { int num; int i; int massimo; int numeri[LUN]; // solita immissione di un array for (i = 0; i < LUN; i = i + 1) { scanf("%d", &num); numeri[i] = num; } // calcolo del massimo massimo = numeri[0]; // cosa serve questa riga? e se la tolgo ??? // perche' prendo numeri[0] ovvero il primo elem.?? for (i = 0; i < LUN; i = i + 1) { if (numeri[i]>massimo) { massimo = numeri[i]; } } printf("massimo=%d\n", massimo); getchar();// non serve a nulla ma ferma la finestra... exit(0); }
Esercizio 12: StampaAlberto
Completando la traccia del main sotto riportata scrivi la procedura mancante stampaAlbero(); allo scopo di stampare il seguente disegno:
La traccia del codice:#include <stdio.h> #include <stdlib.h> int main() { stampaAlbero(); getchar(); exit(0); }