View
213
Download
0
Category
Preview:
Citation preview
Anno accademico 2010-2011Anno accademico 2010-2011
11
Le funzioni in CLe funzioni in C
Anno accademico 2010-2011Anno accademico 2010-2011
22
SommarioSommario
• Le funzioniLe funzioni Il passaggio dei parametri Il passaggio dei parametri Le dichiarazioni e le chiamateLe dichiarazioni e le chiamate I prototipi di funzioneI prototipi di funzione I puntatori a funzioneI puntatori a funzione La ricorsioneLa ricorsione
• Ancora sull’ordinamentoAncora sull’ordinamento• La funzione La funzione main()main()
Anno accademico 2010-2011Anno accademico 2010-2011
33
Le funzioniLe funzioni
Anno accademico 2010-2011Anno accademico 2010-2011
44
• Gli argomenti di una funzione costituiscono il mezzo per passare dati al sottoprogramma
• In C, gli argomenti sono passati per valore per valore (by valueby value), ovvero viene passata una copia dell’argomento
la funzione chiamata può modificare il valore della copia, ma non dell’argomento originale
• L’argomento passato viene detto parametro attualeparametro attuale, mentre la copia ricevuta è il parametro formaleparametro formale
Nella chiamata per indirizzo, i parametri formale ed at-tuale fanno riferimento alla stessa area di memoria; nel caso di chiamata per valore, nel parametro formale va una copia del parametro attuale
Indirizzo del
parametro
Parametro attuale
Parametro attuale
Parametro formale
Parametro formale
Valore del parametro
Passaggio per indirizzoPassaggio per indirizzo
Passaggio per valorePassaggio per valore
Il passaggio dei parametri Il passaggio dei parametri 1 1
Anno accademico 2010-2011Anno accademico 2010-2011
55
• Dato che in C gli argomenti vengono passati per valore, la funzione chiamata può modificare il parametro formale, senza che ciò si ripercuota sul parametro attuale
La funzione printf() printf() non stam-pa 3, ma 2, in quanto il parametro formale received_argreceived_arg in f()f() è una copia del parametro attuale aa
include <stdio.h>include <stdlib.h>
main()
{
extern void f();
int a2; f(a); /* passa una copia di a ad f() */
printf(“%d\n”, a);
exit(0);
}
void f(received_arg)
int received_arg;
{
received_arg 3;}
Il passaggio dei parametri Il passaggio dei parametri 2 2
Anno accademico 2010-2011Anno accademico 2010-2011
66
• In C, il parametro attuale, usato nella chiamata, ed il corrispondente parametro formale, usato nella definizione della funzione, vengono associati, indipendentemente dai nomi adottati Il primo parametro attuale viene a corrispondere al primo
parametro formale, il secondo parametro attuale al secondo parametro formale, etc.
• È necessario che i tipi dei parametri attuali coincidano con quelli dei corrispondenti parametri formali
• Inoltre, se occorre che una funzione modifichi il valore di un oggetto, è necessario passare un puntatore all’oggetto ed assegnare all’oggetto un valore mediante l’operatore di accesso all’indirizzo contenuto nel puntatore
Il passaggio dei parametri Il passaggio dei parametri 3 3
Anno accademico 2010-2011Anno accademico 2010-2011
77
void swap(x, y)
int *x, *y;{ register int temp;
temp *y; *y *x; *x temp;}
main(){ int a2, b3;
swap(&a, &b) printf(“a%d\t b%d\n”, a, b);}
EsempioEsempio
Nota:Nota: Il passaggio degli argomenti per valore chiarisce la necessità di usare l’operatore “indirizzo di”, &&, nelle chiamate alla funzione scanf()scanf() : se venissero passate direttamente le variabili, scanf() scanf() non potrebbe mo-dificarle permanentemente; passando-ne gli indirizzi, scanf() scanf() può accede-re alle variabili ed assegnare i valori letti
L’esecuzione del programma produce
aa33 bb22
Il passaggio dei parametri Il passaggio dei parametri 4 4
Anno accademico 2010-2011Anno accademico 2010-2011
88
• Le funzioni possono apparire in un programma in una delle tre forme seguenti:
DefinizioneDefinizione dichiarazione, che definisce il numero ed il tipo degli argomenti ed il comportamento della funzione
Allusione a funzione Allusione a funzione dichiarazione di una funzione definita altrove, che specifica la natura del valore restituito dalla funzione (con la prototipazioneprototipazione possono essere specificati anche il numero ed il tipo degli argomenti)
Chiamata di funzione Chiamata di funzione invocazione di una funzione, che ha come effetto il trasferimento del controllo del programma alla funzione chiamata; al termine della funzione chiamata, l’esecuzione riprende dall’istruzione (del programma chiamante) immediatamente successiva alla chiamata completata
Le dichiarazioni e le chiamateLe dichiarazioni e le chiamate
Anno accademico 2010-2011Anno accademico 2010-2011
99
• È possibile specificare un nume-ro qualsiasi di argomenti
• Se non altrimenti specificato, il tipo restituito si intende intint
• Se la funzione non restituisce alcun valore, deve essere speci-ficato voidvoid come tipo restituito
Dichiarazione
argomento
Tipo restituito
)
,
Corpo funzione
}
(Nome funzione
{
Argomento
La sintassi della definizione di La sintassi della definizione di funzionefunzione
Anno accademico 2010-2011Anno accademico 2010-2011
1010
• La dichiarazione degli argomenti segue le stesse regole delle dichiarazioni di variabili, con le seguenti eccezioni: La sola classe di memorizzazione ammessa è registerregister Oggetti di tipo charchar e shortshort sono convertiti in int int, mentre
oggetti di tipo floatfloat sono convertiti in doubledouble (con la prototipazioneprototipazione, le conversioni automatiche possono essere evitate)
Un parametro formale dichiarato come array viene convertito in un puntatore ad un oggetto del tipo base dell’array
Un parametro formale dichiarato di tipo funzione viene con-vertito in un puntatore a funzione
In una dichiarazione, non può essere presente una inizializ-zazione
• La dichiarazione di un argomento può essere omessa: il tipo dell’argomento è intint per default
La dichiarazione degli argomentiLa dichiarazione degli argomenti
Anno accademico 2010-2011Anno accademico 2010-2011
1111
• Le funzioni possono restituire direttamente un solo valore, mediante l’istruzione returnreturn
• Il valore restituito può essere di tipo qualunque, eccetto array o funzione
• È possibile restituire indirettamente più di un valore mediante la restituzione di un puntatore ad un tipo composto
• Inoltre, una struttura o un’unione possono essere restituite direttamente (anche se ciò è sconsigliato per motivi di efficienza)
• La sintassi di un’istruzione returnreturn è
Espressione
return
I valori restituiti I valori restituiti 1 1
Anno accademico 2010-2011Anno accademico 2010-2011
1212
• In una funzione può essere presente un numero qualsiasi di istruzioni returnreturn: la prima incontrata nell’evolvere del flusso restituisce il controllo al programma chiamante
• Il valore restituito deve essere compatibile con il tipo della funzione
• Se non esiste alcuna istruzione returnreturn, il controllo del programma ritorna alla funzione chiamante quando si rag-giunge la parentesi graffa chiusa che conclude il corpo della funzione: il valore restituito è indefinitoindefinito
• Un’istruzione returnreturn senza espressione può essere utilizzata (senza effetti collaterali) per restituire il controllo al chiaman-te dall’interno di una funzione dichiarata voidvoid
I valori restituiti I valori restituiti 2 2
Anno accademico 2010-2011Anno accademico 2010-2011
1313
• Esempio:Esempio: valori restituiti da returnreturn, implicitamente con-vertiti dal compilatore, per una funzione che dovrebbe restituire un float float
float f()
{
float f2;
int a;
char c;
f2 a; /* OK, conversione implicita di a in float */ return a; /* OK, conversione implicita di a in float */
f2 c; /* OK, conversione implicita di c in float */ return c; /* OK, conversione implicita di c in float */
}
I valori restituiti I valori restituiti 3 3
Anno accademico 2010-2011Anno accademico 2010-2011
1414
• Esempio: Esempio: istruzioni returnreturn corrette e scorrette nel caso di una funzione che restituisce un puntatore a charchar
char *f(){ char **cpp, *cp1, *cp2, ca[10]; int *ip1, *ip2;
cp1 cp2 /* OK i tipi corrispondono */ return cp2; /* OK i tipi corrispondono */ cp1 *cpp; /* OK i tipi corrispondono */ return *cpp; /* OK i tipi corrispondono */ cp1 ca; /* OK i tipi corrispondono */ return ca; /* OK i tipi corrispondono, ma… */ cp1 *cp2; /* Errore: puntatore a char e char */ return *cp2; /* Errore: puntatore a char e char */ cp1 ip1; /* Errore: i tipi dei puntatori non corrispondono */ return ip1; /* Errore: i tipi dei puntatori non corrispondono */ return; /* provoca un comportamento indefinito */ /* dovrebbe restituire (char *) */
}
I valori restituiti I valori restituiti 4 4
Anno accademico 2010-2011Anno accademico 2010-2011
1515
• Un’allusione a funzione è una dichiarazione di una funzione definita altrove: serve principalmente per informare il compilatore sul tipo restituito dalla funzione
• Poiché, se non specificato, il tipo di una funzione è int int per default, le allusioni a funzioni intere potrebbero essere omesse
• È comunque buona norma inserire le allusioni a tutte le funzioni
possibilità di determinare tutte le funzioni richiamate dalla lettura delle frasi dichiarative, senza dover scandire tutto il sorgente
• Per lo stesso motivo: omettere allusioni a funzioni non utilizzate, per evitare ambiguità
Classe di memorizzazion
e
Nome funzione
Tipo restituito
)(
La sintassi dell’allusione a La sintassi dell’allusione a funzionefunzione
Le allusioni a funzione Le allusioni a funzione 1 1
Anno accademico 2010-2011Anno accademico 2010-2011
1616
• Se la classe di memorizzazione viene omessa, il compilatore assume la classe externextern, corrispondente a una funzione la cui definizione può apparire nello stesso file sorgente o altrove
• L’unica altra classe di memorizzazione ammessa è staticstatic, corrispondente ad una funzione definita nello stesso file
• Il tipo di funzione in un’allusione deve coincidere con il tipo specificato nella definizione della funzione
Le allusioni a funzione Le allusioni a funzione 2 2
Anno accademico 2010-2011Anno accademico 2010-2011
1717
• Se la classe di memorizzazione ed il tipo vengono entrambi omessi, l’espressione rappresenta una chiamata di funzione se appare all’interno di un blocco, un’allusione se all’esterno
f1();
… … …
main()
{
… … …
f2();
… … …
Chiamata a funzione di tipo voidvoid
Allusione a funzione Il tipo di default è intint
Le allusioni a funzione Le allusioni a funzione 3 3
Anno accademico 2010-2011Anno accademico 2010-2011
1818
• Le funzioni con allusioni all’interno di un blocco hanno ambito di visibilità a livello di blocco
• Le funzioni con allusioni all’esterno di un blocco hanno ambito di visibilità a livello di file
• Le regole per la definizione della classe di memorizzazione sono diverse nel caso delle allusioni a funzione, rispetto agli altri tipi di variabili
float func();
float *pfl, arfl[10]; Internamente ad un blocco le variabili sono autoauto, esternamente sono variabili globali
Sia internamente che esternamente ad un blocco la funzione è externextern per default
Le allusioni a funzione Le allusioni a funzione 4 4
Anno accademico 2010-2011Anno accademico 2010-2011
1919
• I prototipi di funzione prototipi di funzione (introdotti nel C) consentono di includere nelle allusioni la specifica del tipo degli argomenti Il compilatore può controllare che il tipo dei parametri
attuali nelle chiamate di funzione sia compatibile con il tipo dei parametri formali specificati nell’allusione
Le conversioni automatiche non sono più necessarie (e non vengono effettuate) migliorando le prestazioni dei programmi che utilizzano pesantemente interi corti e floatingpoint in singola precisione
• Esempi:Esempi:extern void func(int,float,char *);
extern void func(int a,float b,char *pc);
I due prototipi sono uguali: i nomi degli argomenti aumentano la leggibilità, ma non viene loro riservata memoria, né si creano conflitti con variabili omonime
I prototipi di funzione I prototipi di funzione 1 1
Anno accademico 2010-2011Anno accademico 2010-2011
2020
• La prototipazioneprototipazione assicura che venga passato il numero esatto di argomenti e impedisce il passaggio di argomenti che non possono essere convertiti implicitamente nel tipo corretto
• In assenza di argomenti, occorre specificare il tipo voidvoid
• Se una funzione gestisce un numero di argomenti variabile, può essere utilizzata la forma “…”
• Esempio:Esempio: il prototipo di printf()printf() è:
int printf(const char *format,…);int printf(const char *format,…);
che asserisce che il primo elemento è una stringa di caratteri costante, seguita da un numero non specificato di argomenti
I prototipi di funzione I prototipi di funzione 2 2
Anno accademico 2010-2011Anno accademico 2010-2011
2121
• Una chiamata di funzionechiamata di funzione, detta anche invocazione di invocazione di funzionefunzione, trasferisce il controllo del programma alla funzione specificata
• Una chiamata di funzione è un’espressione e può quindi comparire ovunque è ammessa un’espressione
• Salvo il caso in cui il tipo restituito è voidvoid, il valore restituito dalla funzione viene sostituito alla chiamata
• Esempio:Esempio: se f()f() restituisce 1…
),
(
Argomento
Nome funzione
a 1/3;a f()/3;
Le chiamate di funzione Le chiamate di funzione 1 1
Anno accademico 2010-2011Anno accademico 2010-2011
2222
• I valori di ritorno vengono ignorati solo quando la funzione restituisce voidvoid
• Se si desidera ignorare deliberatamente un valore restituito, è buona norma convertirlo esplicitamente in voidvoid
• Questa regola è stata trasgredita ogni volta che sono state usate le funzioni printf()printf() e scanf()scanf(), che restituiscono valori interi (in particolare l’intero restituito da printf()printf() e scanf()scanf() è il numero di oggetti scritti/letti)
f();(void) f();
Le chiamate di funzione Le chiamate di funzione 2 2
Anno accademico 2010-2011Anno accademico 2010-2011
2323
• In mancanza di prototipazione, tutti gli argomenti scalari di dimensioni minori di un int int vengono convertiti in intint e gli argomenti float float vengono convertiti in doubledouble
• Se il parametro formale è dichiarato essere un charchar o uno shortshort o un floatfloat, anche la funzione chiamata, che si aspetta di ricevere intint o doubledouble, rispettivamente, riconverte ai tipi più corti
Ogni volta che viene passato come parametro un charchar, uno short short o un floatfloat vengono effettuate due conversioni, nel programma chiamante e nella funzione chiamata
Le conversioni automatiche Le conversioni automatiche degli argomenti degli argomenti 1 1
Anno accademico 2010-2011Anno accademico 2010-2011
2424
• Nell’ipotesi di coincidenza fra i tipi dei parametri formali ed attuali, gli argomenti vengo-no passati correttamente
• Tuttavia… le conversioni au-tomatiche possono far dimi-nuire l’efficienza del pro-gramma
• La prototipazione evita le conversioni automatiche de-gli argomenti
{ char a; short b; float c; foo(a,b,c); /* a e b vengono convertiti in interi * c viene convertito in double */ … … …}
foo(x,y,z) char x; /* L’argomento ricevuto è convertito da int a char */ short y; /* L’argomento ricevuto è convertito da int a short */ float z; /* L’argomento ricevuto è convertito da double a float */ { … … …}
EsempioEsempio
Le conversioni automatiche Le conversioni automatiche degli argomenti degli argomenti 2 2
Anno accademico 2010-2011Anno accademico 2010-2011
2525
• Nell’allusioneextern int f();extern int f();
il simbolo ff è un puntatore a funzionepuntatore a funzione, che permette di accedere alla locazione riferita dal puntatore; dato che ff è un puntatore costante, non è possibile assegnargli un valore
• Per dichiarare un puntatore variabile a funzione, è necessario far precedere il nome del puntatore da un asterisco
• Esempio:Esempio:
int (*pf)(); /* dichiara un puntatore ad una funzione intera */int (*pf)(); /* dichiara un puntatore ad una funzione intera */
Le parentesi che delimitano *pf *pf sono necessarie per consentire un raggruppamento corretto: se non venissero specificate si otterrebbe la dichiarazione di una funzione che restituisce un puntatore ad intpuntatore ad int
I puntatori a funzioneI puntatori a funzione
Anno accademico 2010-2011Anno accademico 2010-2011
2626
• Per disporre dell’indirizzo di una funzione è sufficiente specificare il nome di funzione, omettendo l’elenco degli argomenti tra parentesi
• Se vengono specificate le parentesi, si ottiene una chiamata di funzionepf f1(); /* SCORRETTO: f1 restituisce un int
mentre pf è un puntatore */pf &f1(); /* SCORRETTO: non si può accedere all’indirizzo del risultato di una funzione */pf &f1; /* SCORRETTO: &f1 è un puntatore a puntatore pf è un puntatore a funzione intera */
{ extern int f1(); int (*pf) (); /* dichiara pf come un puntatore ad una funzione che restituisce un int */ pf f1; /* assegna l’indirizzo di f1 a pf */ … … …
L’assegnamento di valori L’assegnamento di valori ai puntatori a funzioneai puntatori a funzione
Anno accademico 2010-2011Anno accademico 2010-2011
2727
• Corrispondenza obbligatoriaCorrispondenza obbligatoria: se viene dichiarato un puntatore a una funzione che restituisce un intint, a tale puntatore è possibile assegnare solo l’indirizzo di una funzione che restituisce un intint
• Se non vi è corrispondenza di tipo, si ha una segnalazione di errore in compilazione
extern int if1(),if2(),(*pif)();extern float ff1(),(*pff)();extern char cf1(),(*pcf)();
main (){ pif if1; pif cf1; /* SCORRETTO: i tipi non corrispondono */ pff if2; /* SCORRETTO: i tipi non corrispondono */ pcf cf1; if1 if2; /* SCORRETTO: assegnamento a costante */}
La concordanza fra tipiLa concordanza fra tipi
Anno accademico 2010-2011Anno accademico 2010-2011
2828
• Per accedere all’indirizzo contenuto in un puntatore a funzione e richiamare la funzione corrispondente…
…deve essere utilizzata la stessa sintassi della dichiarazione del puntatore a funzione…
…con l’aggiunta delle parentesi tonde e della lista degli argomenti
{ extern int f1(); int (*pf)(); int a, answer;
pf f1; answer (*pf)(a); /* richiama la funzione f1 con argomento a */ … … …
La chiamata di funzioni La chiamata di funzioni mediante puntatori mediante puntatori 1 1
Anno accademico 2010-2011Anno accademico 2010-2011
2929
• Per l’accesso all’indirizzo contenuto in un puntatore a funzione può essere utilizzato un numero qualsiasi di asterischi
perché…
Un nome di funzione è convertito in un puntatore alla funzione
Le parentesi modificano l’ordine di valutazione facendo sì che si valuti per prima l’espressione ****pf****pf: ogni accesso all’indirizzo contenuto in pfpf provoca nuovamente la sua trasformazione in un puntatore, poiché l’elenco degli argomenti non viene considerato; ciò avviene solo quando il compilatore ha analizzato tutti gli operatori **, trasformando l’espressione in una chiamata di funzionepf(a);(*pf)(a);
La chiamata di funzioni La chiamata di funzioni mediante puntatori mediante puntatori 2 2
(****pf)(a);(*pf)(a);
Anno accademico 2010-2011Anno accademico 2010-2011
void bubble_sort(list, list_size)int list[], list_size;{ int j, temp, sortedFALSE; while (!sorted) { sorted TRUE; /* assume list ordinato */ for (j0; j < list_size1; j) { if (list[j] > list[j1]) { /* almeno un elemento non in ordine */ sorted FALSE; temp list[j]; list[j] list[j1]; list[j1] temp; } } /* fine del ciclo for */ } /* fine del ciclo while */}
• ProblemaProblema:: ordinare un array di interi, con possibilità di scelta fra ordinamento crescente e decrescente
• I puntatori a funzione sono impiegati come meccanismo per gestire operazioni simili, evitando la duplicazione di codice
È l’unica istruzione che deve essere modificata: l’operatore relazionale distin-gue i due casi Invece di riscrivere il programma, è più semplice estrarre l’espressione, racchiudendola in una fun-zione compare()compare()
if(compare(list[j],list[jif(compare(list[j],list[j1]))1]))
Una funzione di ordinamento Una funzione di ordinamento 1 1
3030
Anno accademico 2010-2011Anno accademico 2010-2011
3131
• Se l’ordinamento è crescente, compare() compare() deve restituire 1 quando list[j]list[j] è maggiore di list[jlist[j1]1], 0 nel caso contrario; se l’ordinamento e decrescente i valori di ritorno sono invertiti
Le funzioni di confronto sono due:
• Per scegliere fra le due funzioni, occorre trasformare compare() compare() in un puntatore a funzione, in grado di attivare le due funzioni in maniera selettiva, ed aggiungere un argomento a bubble_sort() bubble_sort() per indicare il tipo di ordinamento prescelto
Una funzione di ordinamento Una funzione di ordinamento 2 2
Anno accademico 2010-2011Anno accademico 2010-2011
define FALSE 0define TRUE 1void bubble_sort(list, list_size, compare )int list[], list_size, (*compare)();{ int j, temp, sortedFALSE; while (!sorted) { sorted TRUE; /* assume list ordinato */ for (j 0; j < list_size1; j) { if ((*compare)(list[j], list[j1])) { /* almeno un elemento non in ordine */ sorted FALSE; temp list[j]; list[j] list[j1]; list[j1] temp; } } /* fine del ciclo for */ } /* fine del ciclo while */}
3232
define ASCEND compare_ascenddefine DESCEND compare_descendextern void bubble_sort();
extern int compare_ascend();
extern int compare_descend();
include <stdlib.h>include “sort.h”
main( ){ static int list[]{1,0,5,444,332, 76};
define LIST_SIZE (sizeof(list)/sizeof(list[0]))
bubble_sort(list, LIST_SIZE, DESCEND); exit(0);}
File header sort.hFile header sort.h
Funzione Funzione bubble_sort()bubble_sort()
Programma Programma principaleprincipale
Una funzione di ordinamento Una funzione di ordinamento 3 3
Anno accademico 2010-2011Anno accademico 2010-2011
3333
• Una funzione è ricorsivaricorsiva se richiama se stessa
• Nei programmi ricorsivi, è necessario prevedere una condizione di terminazione, altrimenti il programma non si interrompe
void recurse(){ static count1; printf(“%d\n”, count); count; recurse();}
main( ){ extern void recurse();
recurse();}
La funzione stampa il valore di countcount (che è 1, inizialmente), incrementa countcount ed infine richiama se stessa; la seconda volta count count vale 2… il procedimento viene ripetuto all’infinito
123…
Infine, il calcolatore esaurirà la memoria disponibile per lo stack ed il programma verrà interrotto (con segnalazione di errore)
La ricorsione La ricorsione 1 1
Anno accademico 2010-2011Anno accademico 2010-2011
3434
• La condizione che conclude la ricor-sione è detta condizione basecondizione base
• Se countcount avesse durata automatica anziché fissa, il programma non terminerebbe mai perché, ad ogni nuova chiamata, countcount verrebbe reinizializzata ad 1
• Nella ricorsione, ad ogni nuova chiamata di funzione, il compilatore crea un nuovo insieme completo di variabili automatiche che, pur avendo lo stesso nome, occupano aree di memoria distinte (sullo stack)
void recurse(){ static count1;
if (count > 3) return; else { printf(“%d\n”,count); count; recurse(); }}
main( ){ extern void recurse();
recurse();}
La ricorsione La ricorsione 2 2
Anno accademico 2010-2011Anno accademico 2010-2011
3535
• L’uso di variabili fisse è una delle modalità di controllo L’uso di variabili fisse è una delle modalità di controllo della ricorsione, l’altra è l’uso di valori d’ingressodella ricorsione, l’altra è l’uso di valori d’ingresso
• Esempio: Esempio: Calcolo della somma degli interi da 1 ad n
int sum(n)
int n;
{
if (n < 1) return n;
else
return(nsum(n1));}
Il valore restituito da chiamate Il valore restituito da chiamate ricorsivericorsive
5sum(4)
3sum(2)
sum(5)
4sum(3)
2sum(1)
15
3
1
6
10
Anno accademico 2010-2011Anno accademico 2010-2011
3636
1)1) FattorialeFattoriale
int rfatt(n)
int n;
{
if (n0) return 1;
else
return (nrfatt(n1));}
Funzione ricorsivaFunzione ricorsiva
int fatt(n)
int n;
{
int t, result;
result 1; for (t2; t<n; t) result t; return result;
}
Funzione non Funzione non ricorsivaricorsiva
Esempi di funzioni ricorsive Esempi di funzioni ricorsive 1 1
Anno accademico 2010-2011Anno accademico 2010-2011
3737
2)2) I numeri di FibonacciI numeri di Fibonacci
Si consideri la sequenza di numeri interi 0,1,1,2,3,5,8,13,21…
nota come successione di FibonacciLa legge di generazione della successione è espressa mediante la relazione di ricorrenza
F0 0 F11 Fn Fn1 Fn2
Nota:Nota: La legge di Fibonacci legge di Fibonacci (Leonardo Pisano, detto il Fibonacci, Pisa 1170) descrive la crescita del numero di coppie di conigli a partire da una singola coppia, supponendo che ogni coppia procrei, ogni mese, una nuova coppia di conigli, che sia in grado di riprodursi a partire dal mese successivo
Esempi di funzioni ricorsive Esempi di funzioni ricorsive 2 2
Anno accademico 2010-2011Anno accademico 2010-2011
3838
Calcolo dell’nCalcolo dell’nesimo termine della successione di esimo termine della successione di FibonacciFibonacciint fibonacci (int n);
main (void)
{
int numero;
printf(“\nInserisci il numero d’ordine del termine ”);
scanf(“%d”, &numero);
printf(“L’%desimo numero di Fibonacci è %d”,numero,fibonacci(numero));}
int fibonacci(n)
int n;
{
if (n < 0) return 0; /* passo base: n 0 */ else if (n 1) return 1; /* passo base: n 1 */ else
return (fibonacci(n1) + fibonacci(n2)); /* passo di induzione */}
Prototipo della funzione fibonacci()fibonacci()
Esempi di funzioni ricorsive Esempi di funzioni ricorsive 3 3
Anno accademico 2010-2011Anno accademico 2010-2011
3939
3)3) Algoritmo di Euclide per il calcolo del M.C.D.Algoritmo di Euclide per il calcolo del M.C.D.int mcd(int m, int n);
main (void){ int numero1, numero2; printf(“Inserisci i due numeri ”); scanf(“%d %d”, &numero1, &numero2); printf(“Il MCD tra %d e %d è %d\n”,numero1,numero2,mcd(numero1,numero2));}
int mcd(m, n)int m, n; { if (m 0) return n; /* caso m 0 */ else if ((n 0) || (m n)) return m; /* caso n 0 o m n */ else if (m > n) return mcd(n, m%n); /* caso m>n */ else return mcd(m, n%m); /* caso m<n */}
Prototipo della funzione mcd()mcd()
Esempi di funzioni ricorsive Esempi di funzioni ricorsive 4 4
Anno accademico 2010-2011Anno accademico 2010-2011
4040
85 32 10 71 63 52 21 101 124 152 112 132 149 96 85 32 10 71 63 52 21 101 124 152 112 132 149 96 4848
48 32 10 71 63 52 21 85 124 152 112 132 149 96 48 32 10 71 63 52 21 85 124 152 112 132 149 96 101101
21 32 10 48 63 52 71 85 101 112 96 124 149 152 21 32 10 48 63 52 71 85 101 112 96 124 149 152 132132
10 21 32 48 52 63 71 85 96 101 112 124 132 149 10 21 32 48 52 63 71 85 96 101 112 124 132 149 152152
Come si seleziona l’elemento perno?
4)4) QuicksortQuicksort
Esempi di funzioni ricorsive Esempi di funzioni ricorsive 5 5
Anno accademico 2010-2011Anno accademico 2010-2011
4141
void qsort (int v[], int left, int right);
{
/* ordina v[left]…v[right] in ordine crescente */
int i, last;
extern void swap(int v[], int i, int j);
if (left > right) /* se il vettore ha meno di due elementi */ return; /* si esce dalla funzione */
/* sposta l’elemento discriminante in v[left] */
swap(v, left, (leftright)/2); last left; for (i left1; i < right; i) /* suddivide */ if (v[i]<v[left])
swap(v, last, i);
/* ripristina l’elemento discriminante */
swap(v, left, last);
qsort(v, left, last1); qsort(v, last1, right);}
Sceglie l’elemento mediano come perno
QuicksortQuicksort
Esempi di funzioni ricorsive Esempi di funzioni ricorsive 6 6
void swap(int v[],int i,int j);
{
/* scambia v[i] e v[j] */
int temp;
if (ij) return; temp v[i]; v[i] v[j]; v[j] temp;}
Anno accademico 2010-2011Anno accademico 2010-2011
4242
• Quicksort fornisce le prestazioni migliori quando la disposizione dei dati è tale che, ad ogni passo, l’elemento perno viene collocato alla metà esatta dell’insieme da ordinare
• Sia n 2h1 Ad ogni passo gli elementi separatori dei sottoinsiemi individuati
vengono collocati al centro del sottoinsieme corrispondente Il numero dei confronti ad ogni passo è proporzionale ad n e, in
questo caso, i passi sono esattamente h1 Quicksort, nel caso ottimo, ha complessità dell’ordine di Quicksort, nel caso ottimo, ha complessità dell’ordine di
OO(nlog(nlog22n) n) • Si può dimostrare che tale è anche la complessità nel caso
medio: statisticamente, le configurazioni non ordinate dell’insieme sono preponderanti
• Tuttavia, quando il vettore è già ordinato, occorrono n passi, il Tuttavia, quando il vettore è già ordinato, occorrono n passi, il che fa scadere la complessità del Quicksort a che fa scadere la complessità del Quicksort a OO(n(n22))
Esempi di funzioni ricorsive Esempi di funzioni ricorsive 7 7
Anno accademico 2010-2011Anno accademico 2010-2011
4343
5)5) MergesortMergesort
3434 93 64 25 18 29 76 81 93 64 25 18 29 76 81
3434 93 25 64 18 29 76 81 93 25 64 18 29 76 81
2525 34 64 93 18 29 76 8134 64 93 18 29 76 81
1818 25 29 34 64 76 81 9325 29 34 64 76 81 93
Esempi di funzioni ricorsive Esempi di funzioni ricorsive 8 8
Anno accademico 2010-2011Anno accademico 2010-2011
4444
• L’ordinamento per fusione, Mergesort, è un metodo vantaggioso perché comporta un numero di confronti che è sempre dello stesso ordine, qualsiasi sia la configurazione iniziale del vettore da ordinare
• Mergesort opera, ad ogni passo, partizioni dell’insieme di partenza che hanno la stessa dimensione
• Per n 2h, il numero di passi eseguiti dall’algoritmo è h e, ad ogni passo, vengono effettuati O(n) confronti
• La complessità dell’algoritmo è sempre dell’ordine di La complessità dell’algoritmo è sempre dell’ordine di OO(nlog(nlog22n)n)
Esempi di funzioni ricorsive Esempi di funzioni ricorsive 9 9
Anno accademico 2010-2011Anno accademico 2010-2011
define MAX_DIM 100
void merge(int *v,int b1,int b2,int ei){ int i, j, k, w[MAX_DIM];
i b1; j b2; k b1; /* inizializzazione */ while ((i<b2) && (j<1ei)) if (v[i]<v[j]) w[k] v[i]; else w[k] v[j]; if (i<=b2) do w[k] v[i]; while (i ! b2); else do w[k] v[j]; while (j ! 1ei); for (ib1; i<ei; i) v[i] w[i]; }
4545
MergesortMergesort
Esempi di funzioni ricorsive Esempi di funzioni ricorsive 10 10
void mergesort (int *v, int i, int j){/* ordina v con indici tra i e j */
int k; if (((i1)j) && (v[i]>v[j])) swap(v, i, j); /* passo base */ if ((i1)<j) /* passo di induzione */ { k (ij)/2; mergesort(v, i, k); mergesort(v, k1, j); merge(v,i,k1,j); }}
Anno accademico 2010-2011Anno accademico 2010-2011
4646
• Tutti i programmi C devono contenere una funzione, il main()main(), che è la prima eseguita all’interno di un programma e la cui conclusione determina la fine del programma
• Il compilatore gestisce main() main() come le altre funzioni, con l’eccezione che, al momento dell’esecuzione, l’ambiente deve fornire due argomenti:
argcargc, è un intint che rappresenta il numero di argomenti presenti sulla linea di comando all’atto dell’invocazione del comando
argvargv, è un array di puntatoriarray di puntatori agli argomenti della linea di comando
La funzione La funzione main() main() 1 1
Anno accademico 2010-2011Anno accademico 2010-2011
4747
• Un puntatore al comando stesso è memorizzato in argv[0]argv[0]: nell’esempio, è stato utilizzato l’operatore di incremento prefisso per non visualizzare il comando
• Nei sistemi UNIXUNIX esiste un programma simile, detto echoecho, che visualizza gli argomenti della linea di comando
main (argc, argv)
int argc;
char *argv[];
{
/* visualizza gli argomenti della
* linea di comando */
while (argc > 0) printf(“%s ”, *argv); printf(“\n”);
exit(0);
}
• Quando viene eseguito un programma, gli argomenti della linea di comando devono essere separati da uno o più caratteri di spaziatura
La funzione La funzione main() main() 2 2
Anno accademico 2010-2011Anno accademico 2010-2011
4848
• Gli argomenti della linea di comando sono sempre passati a main() main() come stringhe di caratteri
Se rappresentano valori nu-merici, devono essere conver-titi esplicitamente
Esistono le apposite funzioni della libreria di runtime: atoi()atoi() converte una stringa in intint, atof()atof() converte una stringa in floatfloat, etc.
include <math.h>include <stdlib.h>
main (argc, argv)int argc;char *argv[];{ float x, y;
if (argc < 3) { printf(“Uso: power <number>\n”); printf(“Restituisce arg1 alla arg2\n”); return; } x atof(*argv); y atof(*argv); printf(“%f\n”, pow(x, y));}
La funzione La funzione main() main() 3 3
Recommended