1 / 48

Le funzioni in C

Le funzioni in C. Sommario. Le funzioni Il passaggio dei parametri Le dichiarazioni e le chiamate I prototipi di funzione I puntatori a funzione La ricorsione Ancora sull’ordinamento La funzione main (). 2. Le funzioni. Il passaggio dei parametri  1.

alagan
Télécharger la présentation

Le funzioni in C

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Le funzioni in C

  2. Sommario • Le funzioni • Il passaggio dei parametri • Le dichiarazioni e le chiamate • I prototipi di funzione • I puntatori a funzione • La ricorsione • Ancora sull’ordinamento • La funzione main() 2

  3. Le funzioni

  4. Il passaggio dei parametri  1 • Gli argomenti di una funzione costituiscono il mezzo per passare dati al sottoprogramma • In C, gli argomenti sono passati per valore (byvalue), 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 attuale, mentre la copia ricevuta è il parametro 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 Passaggio per indirizzo Indirizzo del parametro Parametro attuale Parametro formale Passaggio per valore Valore del parametro Parametro attuale Parametro formale

  5. Il passaggio dei parametri  2 • 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 include <stdio.h> include <stdlib.h> main() { extern void f(); int a2; 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; } La funzione printf() non stam-pa 3, ma 2, in quanto il parametro formale received_arg in f() è una copia del parametro attuale a

  6. Il passaggio dei parametri  3 • 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

  7. Il passaggio dei parametri  4 Esempio void swap(x, y) int *x, *y; { register int temp; temp  *y; *y  *x; *x  temp; } main() { int a2, b3; swap(&a, &b) printf(“a%d\t b%d\n”, a, b); } L’esecuzione del programma produce a3 b2 Nota: Il passaggio degli argomenti per valore chiarisce la necessità di usare l’operatore “indirizzo di”, &, nelle chiamate alla funzione scanf(): se venissero passate direttamente le variabili, scanf() non potrebbe mo-dificarle permanentemente; passando-ne gli indirizzi, scanf() può accede-re alle variabili ed assegnare i valori letti

  8. Le dichiarazioni e le chiamate • Le funzioni possono apparire in un programma in una delle tre forme seguenti: • Definizione dichiarazione, che definisce il numero ed il tipo degli argomenti ed il comportamento della funzione • Allusione a funzione  dichiarazione di una funzione definita altrove, che specifica la natura del valore restituito dalla funzione (con la prototipazione possono essere specificati anche il numero ed il tipo degli argomenti) • 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

  9. La sintassi della definizione di funzione • È possibile specificare un nume-ro qualsiasi di argomenti • Se non altrimenti specificato, il tipo restituito si intende int • Se la funzione non restituisce alcun valore, deve essere speci-ficato void come tipo restituito ( Nome funzione ) , Tipo restituito Argomento { Dichiarazione argomento Corpo funzione }

  10. La dichiarazione degli argomenti • La dichiarazione degli argomenti segue le stesse regole delle dichiarazioni di variabili, con le seguenti eccezioni: • La sola classe di memorizzazione ammessa è register • Oggetti di tipo char e short sono convertiti inint, mentre oggetti di tipo float sono convertiti in double (con la prototipazione, 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 è intper default

  11. I valori restituiti  1 • Le funzioni possono restituire direttamente un solo valore, mediante l’istruzione return • 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 return è return Espressione

  12. I valori restituiti  2 • In una funzione può essere presente un numero qualsiasi di istruzioni return: 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 return, 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 è indefinito • Un’istruzione return senza espressione può essere utilizzata (senza effetti collaterali) per restituire il controllo al chiaman-te dall’interno di una funzione dichiarata void

  13. I valori restituiti  3 • Esempio: valori restituiti da return, implicitamente con-vertiti dal compilatore, per una funzione che dovrebbe restituire un 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 */ }

  14. I valori restituiti  4 • Esempio: istruzioni return corrette e scorrette nel caso di una funzione che restituisce un puntatore a char 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 *)*/ }

  15. Le allusioni a funzione  1 • 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 è intper 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à Nome funzione ( ) Classe di memorizzazione Tipo restituito La sintassi dell’allusione a funzione

  16. Le allusioni a funzione  2 • Se la classe di memorizzazione viene omessa, il compilatore assume la classe extern, corrispondente a una funzione la cui definizione può apparire nello stesso file sorgente o altrove • L’unica altra classe di memorizzazione ammessa è static, 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

  17. Le allusioni a funzione  3 • 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(); … … … Allusione a funzione Il tipo di default è int Chiamata a funzione di tipo void

  18. Le allusioni a funzione  4 • 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 Sia internamente che esternamente ad un blocco la funzione è extern per default float func(); float *pfl, arfl[10]; Internamente ad un blocco le variabili sono auto, esternamente sono variabili globali

  19. I prototipi di funzione  1 • I 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 floatingpoint in singola precisione • Esempi: 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 extern void func(int,float,char *); extern void func(int a,float b,char *pc);

  20. I prototipi di funzione  2 • La prototipazione 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 void • Se una funzione gestisce un numero di argomenti variabile, può essere utilizzata la forma “…” • Esempio: il prototipo di printf()è: intprintf(constchar*format,…); che asserisce che il primo elemento è una stringa di caratteri costante, seguita da un numero non specificato di argomenti

  21. a  1/3; a  f()/3; Le chiamate di funzione  1 • Una chiamata di funzione, detta anche invocazione di funzione, 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 è void, il valore restituito dalla funzione viene sostituito alla chiamata • Esempio: se f()restituisce 1… ) Nome funzione ( , Argomento

  22. Le chiamate di funzione  2 • I valori di ritorno vengono ignorati solo quando la funzione restituisce void • Se si desidera ignorare deliberatamente un valore restituito, è buona norma convertirlo esplicitamente in void • Questa regola è stata trasgredita ogni volta che sono state usate le funzioni printf() e scanf(), che restituiscono valori interi (in particolare l’intero restituito da printf()e scanf()è il numero di oggetti scritti/letti) (void) f(); f();

  23. Le conversioni automatiche degli argomenti  1 • In mancanza di prototipazione, tutti gli argomenti scalari di dimensioni minori di un intvengono convertiti in int e gli argomenti floatvengono convertiti in double • Se il parametro formale è dichiarato essere un char o uno short o un float, anche la funzione chiamata, che si aspetta di ricevere int o double, rispettivamente, riconverte ai tipi più corti • Ogni volta che viene passato come parametro un char, uno short o un float vengono effettuate due conversioni, nel programma chiamante e nella funzione chiamata

  24. Le conversioni automatiche degli argomenti  2 • 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 Esempio { 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 */ { … … … }

  25. I puntatori a funzione • Nell’allusione externint f(); il simbolofè un puntatore a funzione, che permette di accedere alla locazione riferita dal puntatore; dato chef è 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: int (*pf)(); /* dichiara un puntatore ad una funzione intera */ Le parentesi che delimitano *pfsono necessarie per consentire un raggruppamento corretto: se non venissero specificate si otterrebbe la dichiarazione di una funzione che restituisce un puntatore ad int

  26. L’assegnamento di valori ai puntatori a funzione • 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 funzione { 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 */ … … … pf  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 */

  27. La concordanza fra tipi • Corrispondenza obbligatoria: se viene dichiarato un puntatore a una funzione che restituisce un int, a tale puntatore è possibile assegnare solo l’indirizzo di una funzione che restituisce un int • Se non vi è corrispondenza di tipo, si ha una segnalazione dierrore 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 */ }

  28. La chiamata di funzioni mediante puntatori  1 • 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 */ … … …

  29. La chiamata di funzioni mediante puntatori  2 • 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: ogni accesso all’indirizzo contenuto in pf 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 funzione (*pf)(a); (****pf)(a); pf(a); (*pf)(a);

  30. Una funzione di ordinamento  1 • Problema: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 void bubble_sort(list, list_size) int list[], list_size; { int j, temp, sortedFALSE; while (!sorted) { sorted  TRUE; /* assume list ordinato */ for (j0; j < list_size1; j) { if (list[j] > list[j1]) { /* almeno un elemento non in ordine */ sorted  FALSE; temp  list[j]; list[j]  list[j1]; list[j1]  temp; } } /* fine del ciclo for */ } /* fine del ciclo while */ } È 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() if(compare(list[j],list[j1]))

  31. Una funzione di ordinamento  2 • Se l’ordinamento è crescente, compare() deve restituire 1 quando list[j] è maggiore di list[j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() in un puntatore a funzione, in grado di attivare le due funzioni in maniera selettiva, ed aggiungere un argomento a bubble_sort() per indicare il tipo di ordinamento prescelto intcompare_descend(a,b) int a, b; /* Confronta a,b restituendo 1 se a<b */ { return a<b; } intcompare_ascend(a,b) int a, b; /* Confronta a,b restituendo 1 se a>b */ { return a>b; }

  32. Una funzione di ordinamento  3 Funzione bubble_sort() define FALSE 0 define TRUE 1 void bubble_sort(list, list_size, compare ) int list[], list_size, (*compare)(); { int j, temp, sortedFALSE; while (!sorted) { sorted  TRUE; /* assume list ordinato */ for (j  0; j < list_size1; j) { if ((*compare)(list[j], list[j1])) { /* almeno un elemento non in ordine */ sorted  FALSE; temp  list[j]; list[j]  list[j1]; list[j1]  temp; } } /* fine del ciclo for */ } /* fine del ciclo while */ } File headersort.h define ASCEND compare_ascend define DESCEND compare_descend extern void bubble_sort(); extern int compare_ascend(); extern int compare_descend(); Programma principale 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); }

  33. La ricorsione  1 • Una funzione è ricorsiva se richiama se stessa • Nei programmi ricorsivi, è necessario prevedere una condizione di terminazione, altrimenti il programma non si interrompe La funzione stampa il valore di count (che è 1, inizialmente), incrementa count ed infine richiama se stessa; la seconda volta countvale 2… il procedimento viene ripetuto all’infinito 1 2 3 … Infine, il calcolatore esaurirà la memoria disponibile per lo stack ed il programma verrà interrotto (con segnalazione di errore) void recurse() { static count1; printf(“%d\n”, count); count; recurse(); } main() { extern void recurse(); recurse(); }

  34. La ricorsione  2 • La condizione che conclude la ricor-sione è detta condizione base • Se count avesse durata automatica anziché fissa, il programma non terminerebbe mai perché, ad ogni nuova chiamata, count verrebbe reinizializzata 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 count1; if (count > 3) return; else { printf(“%d\n”,count); count; recurse(); } } main() { extern void recurse(); recurse(); }

  35. 15 10 6 3 1 Il valore restituito da chiamate ricorsive • L’uso di variabili fisse è una delle modalità di controllo della ricorsione, l’altra è l’uso di valori d’ingresso • Esempio: Calcolo della somma degli interi da 1 ad n sum(5) int sum(n) int n; { if (n < 1) return n; else return(nsum(n1)); } 5sum(4) 4sum(3) 3sum(2) 2sum(1)

  36. Esempi di funzioni ricorsive  1 • Fattoriale int rfatt(n) int n; { if (n0) return 1; else return (nrfatt(n1)); } int fatt(n) int n; { int t, result; result  1; for (t2; t<n; t) result  t; return result; } Funzione ricorsiva Funzione non ricorsiva

  37. Esempi di funzioni ricorsive  2 • I numeri di Fibonacci Si consideri la sequenza di numeri interi 0,1,1,2,3,5,8,13,21… nota come successione di Fibonacci La legge di generazione della successione è espressa mediante la relazione di ricorrenza F0  0 F11 FnFn1Fn2 Nota: La 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

  38. Esempi di funzioni ricorsive  3 Calcolo dell’nesimo termine della successione di Fibonacci int fibonacci (int n); main (void) { int numero; printf(“\nInserisci il numero d’ordine del termine ”); scanf(“%d”, &numero); printf(“L’%desimo 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(n1) + fibonacci(n2)); /* passo di induzione */ } Prototipo della funzione fibonacci()

  39. Esempi di funzioni ricorsive  4 • 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()

  40. Esempi di funzioni ricorsive  5 85 32 10 71 63 52 21 101 124 152 112 132 149 96 48 48 32 10 71 63 52 21 85 124 152 112 132 149 96 101 21 32 10 48 63 52 71 85 101 112 96 124 149 152 132 10 21 32 48 52 63 71 85 96 101 112 124 132 149 152 • Quicksort Come si seleziona l’elemento perno?

  41. Esempi di funzioni ricorsive  6 Quicksort 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, (leftright)/2); last  left; for (i  left1; i < right; i) /* suddivide */ if (v[i]<v[left]) swap(v, last, i); /* ripristina l’elemento discriminante */ swap(v, left, last); qsort(v, left, last1); qsort(v, last1, right); } Sceglie l’elemento mediano come perno void swap(int v[],int i,int j); { /* scambia v[i] e v[j] */ int temp; if (ij) return; temp  v[i]; v[i]  v[j]; v[j]  temp; }

  42. Esempi di funzioni ricorsive  7 • 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  2h1 • 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 h1 • Quicksort, nel caso ottimo, ha complessità dell’ordine di O(nlog2n) • 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 che fa scadere la complessità del Quicksort a O(n2)

  43. Esempi di funzioni ricorsive  8 • Mergesort 3493 64 25 18 29 76 81 3493 25 64 18 29 76 81 2534 64 93 18 29 76 81 1825 29 34 64 76 81 93

  44. Esempi di funzioni ricorsive  9 • 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 O(nlog2n)

  45. Esempi di funzioni ricorsive  10 Mergesort 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<1ei)) 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 ! 1ei); for (ib1; i<ei; i) v[i]  w[i]; } void mergesort (int *v, int i, int j) { /* ordina v con indici tra i e j */ int k; if (((i1)j) && (v[i]>v[j])) swap(v, i, j); /* passo base */ if ((i1)<j) /* passo di induzione */ { k  (ij)/2; mergesort(v, i, k); mergesort(v, k1, j); merge(v,i,k1,j); } }

  46. La funzione main()  1 • Tutti i programmi C devono contenere una funzione, il main(), che è la prima eseguita all’interno di un programma e la cui conclusione determina la fine del programma • Il compilatore gestisce main() come le altre funzioni, con l’eccezione che, al momento dell’esecuzione, l’ambiente deve fornire due argomenti: • argc, è un int che rappresenta il numero di argomenti presenti sulla linea di comando all’atto dell’invocazione del comando • argv, è un array di puntatori agli argomenti della linea di comando

  47. La funzione main()  2 • Un puntatore al comando stesso è memorizzato in argv[0]: nell’esempio, è stato utilizzato l’operatore di incremento prefisso per non visualizzare il comando • Nei sistemi UNIX esiste un programma simile, detto echo, 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

  48. La funzione main()  3 • Gli argomenti della linea di comando sono sempre passati a main() come stringhe di caratteri • Se rappresentano valori nu-merici, devono essere conver-titi esplicitamente • Esistono le apposite funzioni della libreria di runtime: atoi()converte una stringa in int, atof()converte una stringa in float, 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)); }

More Related