Sei sulla pagina 1di 46

Il linguaggio C

Le funzioni
Il passaggio dei parametri
Le dichiarazioni e le chiamate
I prototipi di funzione
I puntatori a funzione
La ricorsione
Ancora sull’ordinamento C
L an
La funzione main() gu
ag
e

Fondamenti di Informatica I  a.a. 2008-09 1


Le funzioni

Fondamenti di Informatica I  a.a. 2008-09 2


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 (by value),
ovvero viene passata una copia dell’argomento
 la funzione chiamata può modificare il valore della copia,
ma non dell’argomento originale
Passaggio per indirizzo Nella chiamata per indirizzo,
Indirizzo del
i parametri formale ed
Parametro Parametro
attuale parametro formale attuale fanno riferimento
alla stessa area di memoria;
Passaggio per valore
nel caso di chiamata per
Parametro Valore del Parametro valore, il parametro formale
attuale parametro formale è una copia del parametro
attuale

L’argomento passato viene detto parametro attuale,


attuale mentre
la copia ricevuta è il parametro formale
Fondamenti di Informatica I  a.a. 2008-09 3
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>
La funzione printf() non stampa main()
3, ma 2, in quanto il parametro {
formale received_arg in f() è una extern void f();
copia del parametro attuale a 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;
Fondamenti di Informatica I  a.a. 2008-09 } 4
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

Fondamenti di Informatica I  a.a. 2008-09 5


Il passaggio dei parametri  4
Esempio
void swap(x, y) L’esecuzione del programma produce
int *x, *y; a3 b2
{
register int temp;
temp  *y;
*y  *x; Nota:
Nota Il passaggio degli argomenti per
*x  temp; valore chiarisce la necessità di usare
} l’operatore “indirizzo di”, &, nelle
main() chiamate alla funzione scanf() : se
{ venissero passate direttamente le
int a2, b3;
variabili, scanf() non potrebbe
modificarle; passandone gli indirizzi,
swap(&a, &b) scanf() può accedere alle variabili ed
printf(“a%d\t b%d\n”, a, b); assegnare i valori letti
}
Fondamenti di Informatica I  a.a. 2008-09 6
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

Fondamenti di Informatica I  a.a. 2008-09 7


La sintassi della
definizione di funzione

Nome funzione ( )

,
Tipo restituito
Argomento

{ Dichiarazione È possibile specificare un


argomento numero qualsiasi di argomenti
Se non altrimenti specificato, il
Corpo funzione
tipo restituito si intende int
Se la funzione non restituisce
} alcun valore, deve essere
specificato void come tipo
restituito
Fondamenti di Informatica I  a.a. 2008-09 8
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 in int,int mentre
oggetti di tipo float sono convertiti in double (con la
prototipazione,
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
convertito in un puntatore a funzione
In una dichiarazione, non può essere presente una
inizializzazione
La dichiarazione di un argomento può essere omessa: il tipo
dell’argomento è int per default

Fondamenti di Informatica I  a.a. 2008-09 9


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
Fondamenti di Informatica I  a.a. 2008-09 10
I valori restituiti  2

In una funzione possono essere presenti un numero qualsiasi


di istruzioni return:
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, return il controllo del
programma ritorna alla funzione chiamante quando si
raggiunge 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
chiamante dall’interno di una funzione dichiarata void

Fondamenti di Informatica I  a.a. 2008-09 11


I valori restituiti  3

Esempio:
Esempio valori restituiti da return,
return implicitamente convertiti
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 */
}

Fondamenti di Informatica I  a.a. 2008-09 12


I valori restituiti  4
Esempio:
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 *) */
}
Fondamenti di Informatica I  a.a. 2008-09 13
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 è 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à
Nome
funzione ( )
Classe di Tipo restituito
memorizzazione

La sintassi dell’allusione a funzione

Fondamenti di Informatica I  a.a. 2008-09 14


Le allusioni a funzione  2
Se la classe di memorizzazione viene omessa, il compilatore
assume la classe extern,
extern corrispondente a una funzione la cui
definizione può apparire nello stesso file sorgente o altrove
L’unica altra classe di memorizzazione ammessa è static, 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
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(); Allusione a funzione
……… Il tipo di default è int
main()
{
………
f2(); Chiamata di funzione 15
Fondamenti di Informatica I  a.a. 2008-09
………
Le allusioni a funzione  3
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
float func(); per default
float *pfl, arfl[10]; Internamente ad un blocco le
variabili sono auto,
auto esternamente
sono variabili globali

Fondamenti di Informatica I  a.a. 2008-09 16


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:
Esempi I due prototipi sono uguali: i
nomi degli argomenti aumentano
extern void func(int, float, char *); la leggibilità, ma non viene loro
riservata memoria, né si creano
extern void func(int a, float b, char *pc); conflitti con variabili omonime

Fondamenti di Informatica I  a.a. 2008-09 17


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() è:
Esempio:
int printf(const char *format, …);
che asserisce che il primo elemento è una stringa di caratteri
costante, seguita da un numero non specificato di argomenti

Fondamenti di Informatica I  a.a. 2008-09 18


Le chiamate di funzione  1
Una chiamata di funzione,
funzione detta anche invocazione di
funzione,
funzione trasferisce il controllo del programma alla funzione
specificata
Nome funzione ( )
,

Argomento

Una chiamata di funzione è un’espressione e può quindi


comparire ovunque è ammessa un’espressione
Salvo il caso in cui il tipo restituito è void,
void il valore restituito
dalla funzione viene sostituito alla chiamata
Esempio:
Esempio se f() restituisce 1…
a  f()/3; a  1/3;
Fondamenti di Informatica I  a.a. 2008-09 19
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

(void) f(); f();

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() /scanf() è il
numero di oggetti scritti/letti)

Fondamenti di Informatica I  a.a. 2008-09 20


Le conversioni automatiche
degli argomenti  1

In mancanza di prototipazione, tutti gli argomenti scalari di


dimensioni minori di un int vengono convertiti in int e gli
argomenti float vengono convertiti in double
Se il parametro formale è dichiarato essere un char o uno
short o un float,
float anche la funzione chiamata, che si aspetta
di ricevere int o double,
double rispettivamente, riconverte ai tipi più
corti
 Ogni volta che viene passato come parametro un char,
char uno
short o un float vengono effettuate due conversioni, nel
programma chiamante e nella funzione chiamata

Fondamenti di Informatica I  a.a. 2008-09 21


Le conversioni automatiche
degli argomenti  2
Esempio
{ Nell’ipotesi di coincidenza fra i
char a;
short b; tipi dei parametri formali ed
float c; attuali, gli argomenti vengono
foo(a,b,c); /* a e b vengono convertiti in interi
* c viene convertito in double
passati correttamente
*/ Tuttavia… le conversioni
………
} automatiche possono far
foo(x,y,z) diminuire l’efficienza del
char x; /* L’argomento ricevuto è convertito programma
da int a char */
short y; /* L’argomento ricevuto è convertito La prototipazione evita le
da int a short */ conversioni automatiche degli
float z; /* L’argomento ricevuto è convertito
da double a float */
argomenti
{
………
}
Fondamenti di Informatica I  a.a. 2008-09 22
I puntatori a funzione

Nell’allusione
extern int f();
il simbolo f è un puntatore a funzione,
funzione che permette di accedere alla
locazione riferita dal puntatore; dato che f è 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 di tipo int */
Le parentesi che delimitano *pf sono necessarie per consentire un
raggruppamento corretto: se non venissero specificate si otterrebbe
la dichiarazione di una funzione che restituisce un puntatore ad int

Fondamenti di Informatica I  a.a. 2008-09 23


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
{
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 */
………
Se vengono specificate le parentesi, si ottiene una chiamata di
funzione
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 */

Fondamenti di Informatica I  a.a. 2008-09 24


La concordanza fra i tipi
Corrispondenza obbligatoria:
obbligatoria se viene dichiarato un
puntatore a una funzione che restituisce un int, 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 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 */
}
Fondamenti di Informatica I  a.a. 2008-09 25
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 */
………

Fondamenti di Informatica I  a.a. 2008-09 26


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

(*pf) (a); (****pf)(a);


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 pf,
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);


Fondamenti di Informatica I  a.a. 2008-09 27
Una funzione di ordinamento  1
I puntatori a funzione sono impiegati come meccanismo per
gestire operazioni simili, evitando la duplicazione di codice
void bubble_sort(list, list_size)
Problema: ordinare un array di
int list[], list_size; interi, con possibilità di scelta fra
{ ordinamento crescente e decrescente
int j, temp, sortedFALSE;
while (!sorted) È l’unica istruzione che deve essere
{ modificata: l’operatore relazionale
sorted  TRUE; /* assume che list sia ordinato */ distingue i due casi
for (j0; j < list_size1; j)
{  Invece di riscrivere il programma,
if (list[j] > list[j1]) è più semplice estrarre l’espressione,
{ /* almeno un elemento non è in ordine */ racchiudendola in una funzione
sorted  FALSE; compare()
temp  list[j];
list[j]  list[j1]; if (compare(list[j],list[j1]))
list[j1]  temp;
}
} /* fine del ciclo for */
} /* fine del ciclo while */ 28
Fondamenti di Informatica I  a.a. 2008-09
}
Una funzione di ordinamento  2
Se l’ordinamento è crescente, compare() deve restituire 1
quando list[j] è maggiore di list[j1],
1] 0 nel caso contrario; se
l’ordinamento e decrescente i valori di ritorno sono opposti
 Le funzioni di confronto sono due:

int compare_ascend(a,b) int compare_descend(a,b)


int a, b; int a, b;
/* /*
*/ Confronta due interi restituendo 1 se a<b
*/ Confronta due interi restituendo 1 se a>b
{ {
return a>b; return a<b;
} }

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
Fondamenti di Informatica I  a.a. 2008-09 29
Una funzione di ordinamento  3
Funzione bubble_sort()
File header sort.h
define FALSE 0
define TRUE 1 define ASCEND compare_ascend
void bubble_sort(list, list_size, compare ) define DESCEND compare_descend
int list[], list_size, (*compare) (); extern void bubble_sort();
{ extern int compare_ascend();
int j, temp, sortedFALSE; extern int compare_descend();
while (!sorted)
{ Programma principale
sorted  TRUE; /* assume che list sia ordinato */
for (j  0; j < list_size1; j) include <stdlib.h>
{ include “sort.h”
if ((*compare) (list[j], list[j1]))
main( )
{ /* almeno un elemento non è in ordine */
{
sorted  FALSE;
static int list[]  {1, 0, 5, 444, 332, 76};
temp  list[j];
define LIST_SIZE (sizeof(list)/sizeof(list[0]))
list[j]  list[j1];
list[j1]  temp; bubble_sort(list, LIST_SIZE, DESCEND);
} exit(0);
} /* fine del ciclo for */ }
} /* fine del ciclo while */
} 30
Fondamenti di Informatica I  a.a. 2008-09
La ricorsione  1
Una funzione è ricorsiva se richiama se stessa
void recurse() La funzione stampa il valore di count (che è
{ 1, inizialmente), incrementa count ed infine
static count1;
richiama se stessa; la seconda volta count
printf(“%d\n”, count);
vale 2… il procedimento viene ripetuto
all’infinito
count;
recurse(); 1
} 2
main( ) 3
{ …
extern void recurse(); Infine, il calcolatore esaurirà la memoria
recurse(); disponibile per lo stack ed il programma
} verrà interrotto (con segnalazione di errore)

Nei programmi ricorsivi, è necessario prevedere una


condizione di terminazione, altrimenti il programma non si
interrompe
Fondamenti di Informatica I  a.a. 2008-09 31
La ricorsione  2

void recurse() La condizione che conclude la


{ ricorsione è detta condizione base
static count1;
Se count avesse durata automatica
if (count > 3)
return; anziché fissa, il programma non
else terminerebbe mai perché, ad ogni
{
nuova chiamata, count verrebbe
printf(“%d\n”, count);
count; reinizializzata ad 1
recurse(); Nella ricorsione, ad ogni nuova
}
} chiamata di funzione, il compilatore
main( ) crea un nuovo insieme completo di
{ variabili automatiche che, pur
extern void recurse();
avendo lo stesso nome, occupano
recurse();
aree di memoria distinte
} 32
Fondamenti di Informatica I  a.a. 2008-09
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:
Esempio Calcolo della somma degli interi da 1 ad n
sum(5) 15
int sum(n)
int n;
5sum(4) 10
{
if (n < 1)
return n; 4sum(3) 6
else
return (nsum(n1)); 3sum(2) 3
}

2sum(1) 1

Fondamenti di Informatica I  a.a. 2008-09 33


Esempi di funzioni ricorsive  1

1) Fattoriale

int fatt (n) int rfatt (n)


int n; int n;
{ {
int t, result; if (n0)
return 1;
result  1;
else
for (t2; t<n; t)
return (nrfatt(n1));
result  t;
}
return result;
}

Funzione non ricorsiva Funzione ricorsiva

Fondamenti di Informatica I  a.a. 2008-09 34


Esempi di funzioni ricorsive  2

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  0F11
Fn  Fn1 Fn2
Nota:
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

Fondamenti di Informatica I  a.a. 2008-09 35


Esempi di funzioni ricorsive  3
Calcolo dell’nesimo termine della successione di Fibonacci

int fibonacci (int n); Prototipo della


funzione fibonacci()
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 */
}
Fondamenti di Informatica I  a.a. 2008-09 36
Esempi di funzioni ricorsive  4
3) Algoritmo di Euclide per il calcolo del M.C.D.
int mcd (int m, int n); Prototipo della
funzione mcd()
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)
{
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 */ 37
Fondamenti di Informatica I  a.a. 2008-09
}
Esempi di funzioni ricorsive  5
4) Quicksort Come si seleziona
l’elemento perno?

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

Fondamenti di Informatica I  a.a. 2008-09 38


Esempi di funzioni ricorsive  6
Quicksort
void qsort (int v[], int left, int right); void swap(int v[], int i, int j);
{ {
/* ordina v[left]…v[right] in ordine crescente */ /* scambia v[i] con v[j] */
int i, last; int temp;
void swap(int v[], int i, int j);
temp  v[i];
if (left > right) /* se il vettore contiene meno di due elementi */
v[i]  v[j];
return; /* si esce dalla funzione */
v[j]  temp;
/* 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);
Sceglie l’elemento mediano
/* ripristina l’elemento discriminante */
come perno
swap(v, left, last);
qsort(v, left, last1);
qsort(v, last1, right);
}
Fondamenti di Informatica I  a.a. 2008-09 39
Esempi di funzioni ricorsive  7
Quicksort fornisce le prestazioni migliori quando la disposizione dei
dati è tale che, ad ogni passo, il separatore 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)

Fondamenti di Informatica I  a.a. 2008-09 40


Esempi di funzioni ricorsive  8

5) Mergesort

34 93 64 25 18 29 76 81

34 93 25 64 18 29 76 81

25 34 64 93 18 29 76 81

18 25 29 34 64 76 81 93

Fondamenti di Informatica I  a.a. 2008-09 41


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)

Fondamenti di Informatica I  a.a. 2008-09 42


Esempi di funzioni ricorsive  10
Mergesort
define MAX_DIM 100
void mergesort (int *v, int i, int j)
void merge(int *v, int b1, int b2, int ei) {
{ /* ordina v con indici tra i e j */
int i, j, k, w[MAX_DIM]; int k;
if (((i1)j) && (v[i]>v[j]))
i  b1; j  b2; k  b1; /* inizializzazione */ swap(v, i, j); /* passo base */
while ((i<b2) && (j<1ei)) if ((i1)<j) /* passo di induzione */
if (v[i]<v[j]) {
w[k]  v[i]; k  (ij)/2;
else mergesort(v, i, k);
w[k]  v[j]; mergesort(v, k1, j);
if (i<b2) merge(v,i,k1,j);
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];
}
Fondamenti di Informatica I  a.a. 2008-09 43
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,
argc è un int che rappresenta il numero di argomenti presenti
sulla linea di comando all’atto dell’invocazione del comando
argv,
argv è un array di puntatori agli argomenti della linea di
comando

Fondamenti di Informatica I  a.a. 2008-09 44


La funzione main()  2

Un puntatore al comando stesso


è memorizzato in argv[0]: argv[0] main (argc, argv)
nell’esempio, è stato utilizzato int argc;
char *argv[];
l’operatore di incremento {
prefisso per non visualizzare il /* visualizza gli argomenti della linea di comando */
while (argc > 0)
comando printf(“%s ”, *argv);

Nei sistemi UNIX esiste un printf(“\n”);


exit(0);
programma simile, detto echo,echo }
che visualizza gli argomenti della
linea di comando
Quando viene eseguito un programma, gli argomenti della linea
di comando devono essere separati da uno o più caratteri di
spaziatura
Fondamenti di Informatica I  a.a. 2008-09 45
La funzione main()  3

Gli argomenti della linea di include <math.h>


comando sono sempre passati include <stdlib.h>

a main() come stringhe di main (argc, argv)

caratteri int argc;


char *argv[];
Se rappresentano valori {
float x, y;
numerici, devono essere if (argc < 3)
convertiti esplicitamente {
printf(“Uso: power <number>\n”);
Esistono le apposite funzioni printf(“Restituisce arg1 elevato ad arg2\n”);
della libreria di runtime: return;
atoi() converte una stringa in }
x  atof(*argv);
int,
int atof() converte una y  atof(*argv);
stringa in float,
float etc. printf(“%f\n”, pow(x, y));
}

Fondamenti di Informatica I  a.a. 2008-09 46

Potrebbero piacerti anche