LE FUNZIONI IN C

Transcript

LE FUNZIONI IN C
LE FUNZIONI IN C
Sommario
1
Introduzione
2
Moduli di programma in C
3
Funzioni di libreria matematiche
4
Funzioni
5
Definizione di funzioni
6
Prototipi di funzioni
7
Intestazione dei file
8
Chiamata delle funzioni: per valore e per indirizzo
9
Ricorsività
10
Esempi di ricorsione: Il Fattoriale e la serie di
Fibonacci
11
Ricorsività e iterazione
 2000 Prentice Hall, Inc. All rights reserved.
Introduzione
– Costruire un programma utilizzando parti o componenti
più piccole
– Ogni parte è più “maneggevole” del programma
originale
• Funzioni
– Sono moduli in C
– Tutti i programmi si scrivono combinando funzioni definite
dall’utente con funzioni di libreria
• La libreria standard del C ha una grande varietà di funzioni
• Rendono il lavoro del programmatore più semplice (non
bisogna inventare di nuovo la ruota!)
 2000 Prentice Hall, Inc. All rights reserved.
I sottoprogrammi
Un sottoprogramma è un insieme di istruzioni identificate mediante un nome, ed
accessibile tramite un'interfaccia, che consente di far comunicare il sottoprogramma
con il (sotto)programma chiamante.
In termini generali ci sono due tipi di sottoprogrammi: quelli che restituiscono un
valore al (sotto)programma chiamante, e quelli che non lo fanno. Ai primi si dà il nome
di funzioni, ai secondi il nome di procedure (o subroutine). Quindi possiamo vedere un
sottoprogramma come un ambiente che (eventualmente) riceve delle informazioni,
svolge l'elaborazione richiesta ed eventualmente restituisce un valore al
(sotto)programma che lo ha chiamato. Graficamente le due tipologie di sottoprogrammi
sono riportate nella figura seguente.
 2000 Prentice Hall, Inc. All rights reserved.
Funzioni in C
In C solitamente la distinzione fra funzioni e procedure viene persa per ciò
che concerne la terminologia, e ci si riferisce più semplicemente ai
sottoprogrammi con il termine funzione, presupponendo che questa
restituisca o meno un valore in base alle specifiche ed al comportamento
che si desidera ottenere.
Quando viene chiamato un sottoprogramma il flusso di esecuzione
abbandona il programma in cui viene fatta la chiamata ed inizia ad eseguire
il corpo del sottoprogramma. Quando quest’ultimo termina la propria
esecuzione, con l'esecuzione dell'istruzione return o con il termine del corpo
del programma, il programma chiamante continua l'esecuzione del codice
che segue la chiamata al sottoprogramma.
Una funzione è una porzione di codice (sequenza di istruzioni) che
vengono raggruppati e a cui viene dato un nome.
Il vantaggio di questa soluzione è che la porzione di codice può essere
utilizzata più volte semplicemente scrivendone il nome.
 2000 Prentice Hall, Inc. All rights reserved.
Si supponga di voler scrivere una funzione che scriva a video un insieme di direttive
per l'utente, che costituiscono il menù del programma, come fatto nello stralcio di
codice riportato:
...
printf("Scegli la voce del menù:\n");
printf("1. addizione\n");
printf("2. sottrazione\n");
printf("3. moltiplicazione\n");
printf("4. divisione\n");
scanf("%d", &scelta);
...
Per rendere la parte di codice una funzione è necessario racchiudere il codice tra un
paio di parentesi graffe per renderle un blocco di codice e dare un nome alla
funzione:
menu()
/*nome della funzione*/
{
printf("Scegli la voce del menù:\n");
printf("1. addizione\n");
printf("2. sottrazione\n");
printf("3. moltiplicazione\n");
printf("4. divisione\n");
}
 2000 Prentice Hall, Inc. All rights reserved.
A questo punto è possibile utilizzare la funzione
chiamandola:
void main()
{
menu();
scanf("%d", &scelta);
...
}
Nel programma, l'istruzione menu(); è equivalente ad aver
scritto direttamente tutte le istruzioni della funzione stessa.
A parte questo semplice esempio, le funzioni hanno lo scopo
di rendere un lungo programma una collezione di porzioni di
codice separate su cui lavorare in modo isolato, suddividendo
la soluzione di un problema complesso in tanti piccoli
sottoproblemi, di più facile soluzione.
 2000 Prentice Hall, Inc. All rights reserved.
Funzioni
• Le funzioni
– Suddividono un programma in moduli
– Tutte le variabili dichiarate all’interno di una funzione sono
“locali”, cioè sono note solo internamente alla funzione
– I parametri di una funzione
• Comunicano informazioni fra le funzioni
• Sono variabili locali
• Vantaggi
– “Divide et impera”
• Rendono lo sviluppo di programmi più maneggevole
– Costituiscono del software riutilizzabile
• Funzioni già esistenti servono come blocchi costitutivi di nuovi
programmi
• Astrazione – nascondono i dettagli interni (ad es. le funzioni di
libreria)
– Evitano di dover ripetere più volte parti di codice uguali
 2000 Prentice Hall, Inc. All rights reserved.
Funzioni in C (cont.)
• “Chiamata” di funzioni
• Sono necessari il nome della funzione ed i suoi argomenti o
parametri (dati)
• La funzione esegue operazioni o manipolazioni sui dati
• La funzione restituisce i risultati di tali elaborazioni
 2000 Prentice Hall, Inc. All rights reserved.
Funzioni di libreria matematiche
• Le funzioni di libreria matematiche
– Eseguono i calcoli matematici più comuni
– #include <math.h>
• Formato della chiamata di funzioni
Nome_della_Funzione (argomenti);
• Per più di un argomento, si usa il ;
– Es.: printf( "%.2f", sqrt( 900.0 ) );
• Chamata della funzione sqrt, che restituisce la radice quadrata del
suo argomento
• Tutte le funzioni matematiche restituiscono variabili di tipo double
– Gli argomenti possono essere costanti, variabili, o espressioni
 2000 Prentice Hall, Inc. All rights reserved.
Funzioni e variabili locali
Nella funzione menu, vista prima per chiarire la filosofia di
fondo delle funzioni, non ci sono variabili e questo è un caso
particolarmente semplice.
Una funzione è una sottounità di un programma completo: il
main stesso è una funzione, chiamata dal sistema. Quindi
anche una funzione avrà in generale delle variabili che
verranno utilizzate al suo interno per effettuare le elaborazioni
desiderate.
Le variabili dichiarate all'interno di una funzione sono
significative e visibili esclusivamente all'interno della
funzione stessa, ed il programma chiamante non ne ha alcuna
visibilità. Si tratta di variabili locali alla funzione stessa.
Le variabili che una funzione dichiara sono create quando la
funzione viene chiamata e vengono distrutte quando la
funzione termina.
 2000 Prentice Hall, Inc. All rights reserved.
Si consideri il seguente esempio:
void main()
{
contastampe();
/*1a chiamata di contastampe */
...
contastampe();
/* 2a chiamata di contastampe */
}
void contastampe()
/* funzione contastampe */
{
int num_stampe;
num_stampe = 0;
printf("xxxxxxx");
num_stampe++;
}
Ad ogni chiamata della funzione contastampe() la variabile num_stampe
viene ricreata e distrutta al termine.
 2000 Prentice Hall, Inc. All rights reserved.
Variabili globali e locali
int x;
f()
{
int x;
x = 1;
{
int x;
x = 2;
}
x = 3;
}
/* nome globale */
/*funzione f */
/* x locale che nasconde x globale */
/* assegna 1 a x locale */
/* nasconde il primo x locale */
/* assegna 2 al secondo x locale */
/* assegna 3 al primo x locale */
scanf ("%d", &x); /* inserisce un dato in x globale */
 2000 Prentice Hall, Inc. All rights reserved.
Parametri
Se si desidera che un valore calcolato da una funzione resti disponibile anche dopo il
termine dell'esecuzione della funzione è necessario che questo venga trasmesso al
programma chiamante.
In modo analogo, se la funzione deve elaborare dei dati del programma chiamante è
necessario che i dati le vengano passati.
A questo scopo vengono definite variabili speciali, chiamate parametri che vengono
utilizzati per passare i valori alla funzione. I parametri vengono elencati nelle parentesi
tonde che seguono il nome della funzione, indicando il tipo ed il nome di ogni parametro.
La lista dei parametri ha come separatore la virgola. Esempio:
somma(int a, int b)
{
int risultato;
risultato = a + b;
}
Questo codice definisce una funzione chiamata somma con due parametri a e b, entrambi di
tipo intero. La variabile risultato è dichiarata localmente alla funzione, nel corpo della
funzione.
I parametri a e b vengono utilizzati all'interno della funzione come normali variabili - si noti
che non devono essere definite due variabili locali a e b. Inoltre, questi a e b non hanno
nulla a che fare con altre variabili a e b dichiarate in altre funzioni.
 2000 Prentice Hall, Inc. All rights reserved.
PARAMETRI E VARIABILI
La differenza fondamentale tra i parametri e le variabili è che
i primi hanno un valore iniziale quando la funzione viene
eseguita, mentre le variabili devono essere inizializzate.
Quindi,
somma(l, 2);
è una chiamata alla funzione somma in cui il parametro a
vale 1 e b vale 2.
È anche possibile far assumere ai parametri il risultato di un
espressione, come ad esempio:
somma(x+2, z*10);
che farà assumere ad a il valore pari a x+2 (in base a quanto
varrà x al momento della chiamata e b pari a z*10.
Più semplicemente si può fissare il valore di un parametro al
valore di una variabile:
somma(x, y);
in cui a assume il valore di x e b di y.
 2000 Prentice Hall, Inc. All rights reserved.
In modo duale è anche necessario o possibile trasmettere al programma chiamante il valore
calcolato dalla funzione. La via più semplice è la restituzione del valore attraverso il nome
della funzione, ossia è come se il nome della funzione fosse una variabile dotata di un
valore. Il valore viene restituito per mezzo della seguente istruzione:
return(value);
Questa istruzione può essere posizionata in qualunque punto della funzione, tuttavia
un'istruzione return causa il termine della funzione e restituisce il controllo al programma
chiamante.
È’ necessario aggiungere un'informazione relativa al tipo di dato che la funzione restituisce.
Con riferimento alla funzione somma, il tipo restituito è un intero; si scriverà dunque:
int sum(int a, int b)
{
...
}
La funzione completa è:
int sum(int a, int b)
{
int risultato;
risultato = a + b;
return (risultato);
}
La chiamata assume quindi la seguente forma:
r = sum(1, 2);
istruzione che somma 1 a 2 e memorizza il risultato nella variabile r (dichiarata int nel
programma chiamante).
 2000 Prentice Hall, Inc. All rights reserved.
Ovviamente, la situazione tra ingressi ed uscite di una funzione non è
uguale: è possibile passare un numero di parametri d'ingresso qualsiasi,
mentre la funzione può restituire mediante l'istruzione return un singolo
valore.
Si noti che una funzione può avere quante istruzioni return si desidera,
ma ciò non consente di restituire più di un valore in quanto quando si
esegue la prima return la funzione termina. (Per poter restituire più dati
è necessario utilizzare un passaggio dell'indirizzo, come vedremo più
avanti).
Per riassumere, una funzione ha la seguente forma sintattica:
tipo_restituito NomeFunzione(lista tipo-nome)
{
istruzioni
}
Nel caso in cui una funzione non restituisca alcun parametro, il tipo
indicato è void, che indica appunto tale eventualità. La funzione void
menu() è una funzione senza parametri d'ingresso e che non restituisce
alcun valore.
 2000 Prentice Hall, Inc. All rights reserved.
Definizione di funzioni
• Formato della definizione di funzioni
Tipo_del _valore _di _ritorno nome_della_funzione( lista_dei_parametri )
{
dichiarazioni e istruzioni
}
– Dichiarazioni e istruzioni: corpo della funzione (blocco)
• Le variabili possono essere dichiarate all’interno dei blocchi
(possono essere annidati)
• Le funzioni non possono essere definite all’interno di altre
funzioni
– Restituzione del controllo alla funzione chiamante
• Se non viene restituito alcun valore:
– return;
– Oppure parentesi graffa }
• Se viene restituito qualcosa:
– return espressione;
 2000 Prentice Hall, Inc. All rights reserved.
Funzioni e prototipi
Dove va scritta la definizione di una funzione, prima o dopo il main()? L'unico
requisito è che la tipologia della funzione (tipo di dato restituito e tipo dei
parametri) sia nota prima che la funzione venga usata.
Una possibilità è scrivere la definizione della funzione prima del main(). Con
questa soluzione è però necessario prestare molta attenzione all'ordine con cui si
scrivono le funzioni, facendo sempre in modo che una funzione sia sempre
definita prima che qualche altra la chiami. In alternativa, la soluzione più pulita
è dichiarare la funzione prima del main, separatamente da dove viene poi
definita. Per esempio:
int somma(int,int);
/*prototipo o dichiarazione della funzione*/
void main()
{
...
}
Qui si dichiara il nome della funzione somma e si indica che restituisce un int.
A questo punto la definizione della funzione può essere messa ovunque.
Per quanto riguarda i parametri ricevuti in ingresso è necessario dichiararne la
tipologia ma non il nome, come mostrato nel seguente esempio:
int restodivisione(int, int);
 2000 Prentice Hall, Inc. All rights reserved.
Proptotipi di funzioni e file di intestazione
• Prototipi di funzioni (dichiarazione)
– Nome della funzione
– Parametri (ciò che viene passato alla funzione)
– Tipo di ritorno – tipo dei dati che la funzione restituisce (default
int)
– I prototipi sono necessari solo se la definizione della funzione
segue il suo uso nel programma chiamante
int maximum( int, int, int );
• Ha tre valori in ingresso di tipo int
• Restituisce un int
• File di intestazione
– Contiene i prototipi dielle funzioni di libreria
– <stdlib.h> , <math.h> , etc
– Si carica con: #include <filename>
#include <math.h>
 2000 Prentice Hall, Inc. All rights reserved.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/* Calcola il massimo fra tre interi */
#include <stdio.h>
int maximum( int, int, int );
Outline
/* prototipo della funzione */
int main()
{
int a, b, c;
printf( "Enter three integers: " );
scanf( "%d%d%d", &a, &b, &c );
printf( "Maximum is: %d\n", maximum( a, b, c ) );
Prototipo della
funzione maximum:
ha 3 parametri di tipo
intero e restituisce un
intero
Input dei dati
return 0;
}
/* Definizione della funzione maximum */
int maximum( int x, int y, int z )
{
int max = x;
if ( y > max )
max = y;
Chiamata della
funzione
Definizione della
funzione
if ( z > max )
max = z;
Output del programma
return max;
}
Enter three integers: 22 85 17
Maximum is: 85
 2000 Prentice Hall, Inc. All rights reserved.
Cubo di un numero
#include <stdio.h>
double cubo(float);
/*dichiarazione (prototipo)*/
main()
{
float a;
double b;
printf("Inserisci un numero: ");
scanf("%f", &a);
b = cubo(a);
/*chiamata*/
printf("%f elevato al cubo e' uguale a %f", a, b);
}
double cubo(float c)
/*definizione*/
{
return (c*c*c);
}
 2000 Prentice Hall, Inc. All rights reserved.
#include <stdio.h>
/*dichiarazione delle funzioni x2,x3,x4,x5,potenza*/
double quad(float);
double cubo(float);
double quar(float);
double quin(float);
double pote(float, int);
double cubo(float c)
{
return(c*c*c);
}
double quar(float c)
{
main()
{
int base, esponente;
double ptnz;
printf(" Inserire base: " );
scanf("%d", &base);
printf(" Inserire esponente (0-5): ");
scanf("%d", &esponente);
/*chiamata della funzione pote*/
ptnz = pote( base, esponente);
if (ptnz == -1)
printf("Potenza non prevista\n");
else
printf("La potenza %d di %d e' %f \n", esponente, base, ptnz);
}
/*definizione delle funzioni*/
double quad(float c)
{
return(c*c);
}
return(c*c*c*c);
}
double quin(float c)
{
return(c*c*c*c*c);
}
double pote(float b, int e)
{
switch (e) {
case 0: return (1);
case 1: return (b);
case 2: return (quad( b ));
case 3: return (cubo( b ));
case 4: return (quar( b ));
case 5: return (quin( b ));
default : return (-1);
}
 2000 Prentice Hall, Inc. All rights reserved.
}
Area di un poligono: triangolo o rettangolo
#include <stdio.h>
double area(float, float, char);
main()
{
float b, h;
double a;
char p;
printf("Inserire poligono (Triangolo/Rettangolo): “);
scanf("%c", &p);
printf("\nInserire base: ");
scanf("%f", &b);
printf("\nInserire altezza : ");
scanf("%f", &h);
a = area(b, h, p);
printf("Il poligono (b = %f, h = %f) ha area %f\n",
b, h, a);
}
 2000 Prentice Hall, Inc. All rights reserved.
double area(float base, float altezza,
char poligono)
{
switch (poligono) {
case 'T': return (base * altezza/2.0);
case 'R': return (base * altezza);
default :
}
}
return -1;
#include <stdio.h>
double area(float, float, char);
main()
/*calcolo dell’area del triangolo e del rettangolo*/
{
float b, h;
double tri, ret;
printf("Inserire base: ");
scanf("%f", &b);
printf("Inserire altezza: ");
scanf("%f", &h);
tri = area(b, h, 'T');
ret = area(b, h, 'R');
printf("Il triangolo (b = %f, h = %f) ha area %f\n", b, h, tri);
printf("Il rettangolo (b = %f, h = %f) ha area %f\n", b, h, ret);
}
double area(float base, float altezza, char poligono)
{
switch (poligono) {
case 'T': return (base * altezza/2.0);
case 'R': return (base * altezza);
default : return -1;
}
}
 2000 Prentice Hall, Inc. All rights reserved.
#include <stdio.h>
char str[] = "Lupus in fabula";
int lung_string(void);
/*funzione che conta gli elementi della frase
(stringa) */
main()
{
int l;
l = lung_string();
printf("La stringa %s ha %d caratteri\n", str, l);
}
int lung_string(void)
{
int i;
for (i = 0; str[i] != '\0'; i++);
return i;
}
 2000 Prentice Hall, Inc. All rights reserved.
/*\0 è il carattere di ‘fine stringa’*/
#include <stdio.h>
/*divisione di due numeri con messaggio di
errore */
void messErr( void );
void messErr( void )
/*va a capo per 20 volte */
{
int i;
main()
{
int a, b, c;
printf("Inserire dividendo:");
scanf("%d", &a);
printf("Inserire divisore:");
scanf("%d", &b);
if (b != 0) {
c = a/b;
printf("%d diviso %d = %d\n", a, b, c);
}
else
messErr();
}
 2000 Prentice Hall, Inc. All rights reserved.
char c;
for (i = 0; i <= 20; i++) printf("\n");
printf("ERRORE!
DENOMINATORE NULLO");
printf("\n Premere un tasto per
continuare\n");
scanf("%c", &c);
}
La ricorsione
La ricorsione è una tecnica di programmazione in cui la risoluzione di problemi di
grandi dimensioni viene fatta mediante la soluzione di problemi più piccoli della stessa
forma. È importante capire che il problema verrà scomposto in problemi della stessa
natura.
Esempio
Si pensi ad una delle prospettive di guadagno che talvolta vengono pubblicizzate: il
vostro compito è di raccogliere 1.000.000 di euro.
Visto che è praticamente impossibile pensare di trovare una persona che versi l'intera
cifra si deve pensare di raccogliere l'intera cifra mediante la somma di contributi più
piccoli.
Se ad esempio si sa che ogni persona interpellata di solito è disposta a metterci 100
euro, è necessario trovare 10.000 persone e chiedere a ciascuna di queste 100 euro.
Trovare 10.000 persone potrebbe però essere un po’ difficile. La soluzione è nel cercare
di trovare altre persone che si dedichino alla raccolta dei soldi, e di delegare a loro la
raccolta di una certa cifra.
Per esempio si può pensare di individuare 10 persone, ognuna delle quali raccolga
100.000 euro. Se anche queste dieci persone adottano la stessa strategia, queste
recluteranno 10 persone, ciascuna delle quali deve raccogliere 10.000 euro.
Lo stesso ragionamento si può ripetere, fino ad arrivare ad avere dieci persone che
raccolgano per un delegato i 100 euro.
 2000 Prentice Hall, Inc. All rights reserved.
La ricorsione (cont.)
Se cerchiamo di codificare questa strategia in pseudo-codice, l'algoritmo
risultante è il seguente:
void RaccogliDenaro(int n)
{
if(n <= 100)
Chiedi i soldi ad una sola persona
else
{
trova dieci volontari
Ad ogni volontario chiedi di raccogliere n/10 euro
Somma i contributi di tutti i dieci volontari
}
}
La cosa che è importante notare è che l’istruzione:
Ad ogni volontario chiedi di raccogliere n/10 euro
non è altro che il problema iniziale, ma su una scala più piccola. Il
compito è lo stesso - raccogliere n euro - solo con un n più piccolo.
 2000 Prentice Hall, Inc. All rights reserved.
La ricorsione (cont.)
Inoltre, siccome il problema è lo stesso, lo si può risolvere chiamando il
sottoprogramma originale.
Quindi, nello pseudo-codice, si può pensare di scrivere:
void RaccogliDenaro(int n)
{
if(n <= 100)
Chiedi i soldi ad una sola persona
else
{
trova dieci volontari
Ad ogni volontario
RaccogliDenaro(n/10)
Somma i contributi di tutti i dieci volontari
}
}
Alla fine, il sottoprogramma RaccogliDenaro finisce per chiamare se
stesso se il contributo da raccogliere è inferiore a 100 euro.
 2000 Prentice Hall, Inc. All rights reserved.
La ricorsione (cont.)
Nel contesto della programmazione, avere un sottoprogramma che chiama se
stesso prende il nome di ricorsione.
Lo scopo dell'esempio è dunque quello di illustrare l'idea di affrontare la soluzione
di un problema facendo riferimento allo stesso problema su scala ridotta.
I problemi che possono essere risolti tramite una forma ricorsiva hanno le seguenti
caratteristiche:
•Uno o più casi semplici del problema hanno una soluzione immediata, non
ricorsiva;
•Il caso generico può essere ridefinito in base a problemi più vicini ai casi
semplici;
•Applicando il processo di redifinizione ogni volta che viene chiamato il
sottoprogramma ricorsivo il problema viene ridotto al caso semplice, facile da
risolvere.
L'algoritmo ricorsivo avrà spesso la seguente forma:
if(è il caso semplice)
risolvilo
else
ridefinisci il problema utilizzando la ricorsione
Quando si individua il caso semplice, la condizione indicata nell'if per la gestione
del caso semplice si chiama condizione di terminazione.
 2000 Prentice Hall, Inc. All rights reserved.
La ricorsione
La figura seguente illustra tale approccio: la soluzione del caso semplice è
rappresentata dalla soluzione del problema di dimensione 1.
 2000 Prentice Hall, Inc. All rights reserved.
Ricorsività
• Funzioni ricorsive
– Funzioni che richiamano se stesse
– Possono risolvere solo un “caso base”
– Dividono il problema in:
• Quello che può essere fatto
• Quello che non può essere fatto – simile al problema originale
– Lancia una nuova copia di se stesso (passo della
ricorsione)
• Quando il “caso base” è risolto
– La funzione è attivata, svolge i vari passi e risolve il problema
 2000 Prentice Hall, Inc. All rights reserved.
Esempio: la moltiplicazione
Come altro esempio si prenda in considerazione il
seguente problema: effettuare il prodotto tra due
numeri a e b conoscendo l'operatore somma ma non
l'operatore prodotto e sapendo solo che:
•un numero moltiplicato per uno è uguale a se stesso
•il problema del prodotto a * b può essere spezzato
in due parti:
P1. il prodotto a * (b-1)
P2. somma a al risultato della soluzione del
problema P1.
 2000 Prentice Hall, Inc. All rights reserved.
Esempio (cont.)
Il problema P1, per quanto non risolubile (perchè
continuiamo a non conoscere l'operatore prodotto) è più
vicino al caso semplice, ed è possibile pensare di spezzare
anche tale problema in due parti, ottenendo così la seguente
cosa:
P1. Prodotto a * (b-1)
P1.1. Prodotto a * (b-2)
P1.2. Somma a al risultato del problema P1.1
P2. Somma a al risultato del problema P1.
Si procede così fino a quando:
P1.1...1. Prodotto a *1
P1.1...2. Somma a al risultato del problema P1...1
Il caso semplice si ha quando n == 1 è vera.
 2000 Prentice Hall, Inc. All rights reserved.
Esempio (cont.)
La forma della ricorsione prima indicata diventa quindi:
if(b == 1)
ris = a;
/* caso semplice */
else
ris = a + moltiplica(a, b-1);
/* passo ricorsivo */
Il codice completo è quindi:
int moltiplica(int a, int b)
{
int ris;
if(b == 1)
ris = a;
else ris = a + moltiplica(a, b-1);
/* passo ricorsivo */
return(ris);
}
Una classe di problemi per cui la ricorsione risulta una soluzione
interessante è quella che coinvolge delle sequenze (o liste) di
elementi di lunghezza variabile.
 2000 Prentice Hall, Inc. All rights reserved.
Record di attivazione
Il record di attivazione mostra il valore dei parametri per ogni chiamata e l'esecuzione della
funzione. Si consideri la chiamata seguente: moltiplica(6,3); la traccia dell'esecuzione è mostrata
nella figura seguente.
 2000 Prentice Hall, Inc. All rights reserved.
Pila di sistema (stack)
Per maggior chiarezza nell’esempio sono stati rappresentati degli
spazi di memoria distinti, anche se in realtà il compilatore mantiene
un’unica pila di sistema (o stack).
Ogni volta che una funzione viene chiamata, i suoi parametri e le
sue variabili locali vengono messi sullo stack, insieme all'indirizzo
di memoria dell'istruzione che effettua la chiamata. Questo
indirizzo serve per sapere a che punto rientrare dalla chiamata a
sottoprogramma.
L'esecuzione dell'istruzione return in uscita dalla funzione svuota
lo stack restituendo il valore che c'era in cima allo stack.
In questo modo, ogni chiamata a funzione, anche quelle ricorsive,
riserva alla funzione spazio necessario per i parametri e per le
variabili locali, cosicché tutto possa funzionare correttamente nel
rispetto delle regole di visibilità.
 2000 Prentice Hall, Inc. All rights reserved.
Esempio: fattoriale
• Fattoriale:
5! = 5 * 4 * 3 * 2 * 1
Si noti che
5! = 5 * 4!
4! = 4 * 3! ...
– Quindi il fattoriale può essere calcolaro ricorsivamente
– Si risolve il caso base (1! = 0! = 1) e si “attiva” la sequenza delle
chiamate
• 2! = 2 * 1! = 2 * 1 = 2;
• 3! = 3 * 2! = 3 * 2 = 6;
 2000 Prentice Hall, Inc. All rights reserved.
Calcolo del fattoriale con una funzione ricorsiva
/* Calcolo del fattoriale con una funzione ricorsiva */
#include <stdio.h>
int fat(int);
/* dichiarazione */
main()
{
int n;
printf("CALCOLO DI n!\n\n");
printf("Inser. n: \t");
scanf("%d", &n);
printf("Il fattoriale di: %d ha valore: %d\n", n, fat(n));
}
int fat(int n)
/*definizione*/
{
if(n==0)
return(1);
else
return(n*fat(n-1));
}
 2000 Prentice Hall, Inc. All rights reserved.
/*chiamata*/
Esempio: serie di Fibonacci
Serie di Fibonacci : 0, 1, 1, 2, 3, 5, 8...
Il rapporto dei numeri consecutivi converge a
“rapporto aureo”.
Ogni numero è la somma dei due precedenti:
fib(n) = fib(n-1) + fib(n-2)
fib(0)=0; fib(1)=1
E’ una formula ricorsiva.
long fibonacci(long n) /*long= intero lungo */
{
if (n==0 || n==1) /*caso base*/
return n;
else return fibonacci(n-1) + fibonacci(n-2);
}
 2000 Prentice Hall, Inc. All rights reserved.
1.618…, detto
Fibonacci
/* Calcolo dei numeri di Fibonacci */
#include <stdio.h>
long int fibo(int);
main()
{
int n;
printf("Successione di Fibonacci f(0)=1 f(1)=1 f(n)=f(n-1)+f(n-2)");
printf("\nInserire n: \t");
scanf("%d", &n);
printf("Termine della successione di argomento %d: %d\n", n, fibo(n));
}
long int fibo(int n)
{
if(n==0)
return(0);
else if(n==1) return(1);
else return(fibo(n-1)+fibo(n-2));
}
 2000 Prentice Hall, Inc. All rights reserved.
Fibonacci: chiamate ricorsive
1° chiamata
f( 3 )
2° chiamata
return
return
f( 1 )
f( 2 )
+
f( 0 )
3° chiamata
return 1
 2000 Prentice Hall, Inc. All rights reserved.
return 0
+
f( 1 )
return 1
3° chiamata
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/* Fig. 5.15: fig05_15.c
Recursive fibonacci function */
#include <stdio.h>
long fibonacci( long );
int main()
{
long result, number;
printf( "Enter an integer: " );
scanf( "%ld", &number );
result = fibonacci( number );
printf( "Fibonacci( %ld ) = %ld\n", number, result );
return 0;
Outline
Prototipo della
funzione
Inizializzazione delle
variabili
Input dei dati (un
numero intero)
Chiamata della
funzione Fibonacci
}
Output dei risultati
/* Recursive definition of function fibonacci */
long fibonacci( long n )
{
if ( n == 0 || n == 1 )
return n;
else
return fibonacci( n - 1 ) + fibonacci( n - 2 );
}
Enter an integer: 0
Fibonacci(0) = 0
Enter an integer: 1
Fibonacci(1) = 1
 2000 Prentice Hall, Inc. All rights reserved.
Definizione della
funzione ricorsiva
Fibonacci
Risultati
Enter an integer: 2
Fibonacci(2) = 1
Outline
Enter an integer: 3
Fibonacci(3) = 2
Enter an integer: 4
Fibonacci(4) = 3
Enter an integer: 5
Fibonacci(5) = 5
Enter an integer: 6
Fibonacci(6) = 8
Enter an integer: 10
Fibonacci(10) = 55
Enter an integer: 20
Fibonacci(20) = 6765
Enter an integer: 30
Fibonacci(30) = 832040
Enter an integer: 35
Fibonacci(35) = 9227465
 2000 Prentice Hall, Inc. All rights reserved.
Program Output
Ricorsione ed iterazione
• Ripetizione
– Iterazione: ciclo esplicito
– Ricorsione: chiamate ripetute a funzione
• Terminazione
– Itarazione: La condizione del ciclo non è più verificata
– Ricorsione: viene riconosciuto il ciclo base
• Criterio di utilizzo
– Si deve scegliere fra efficienza (iterazione) e buona ingegneria del
software (ricorsione)
 2000 Prentice Hall, Inc. All rights reserved.
GLI ARRAY
Introduzione
Array
Dichiarare gli Array
Esempi di utilizzo di Array
Array multidimensionali
Passaggio di Array a Funzioni
 2000 Prentice Hall, Inc. All rights reserved.
Gli array
Le variabili semplici, capaci di contenere un solo
valore, sono utili ma spesso insufficienti per
numerose applicazioni.
Quando si ha la necessità di trattare un insieme
omogeneo di dati esiste un'alternativa efficiente e
chiara all'utilizzo di numerose variabili dello stesso
tipo, da identificare con nomi diversi: definire un
array, ovvero una collezione di variabili dello stesso
tipo, che costituisce una variabile strutturata.
strutturata
L'array costituisce una tipologia di dati strutturata e
statica:
statica la dimensione è fissata al momento della sua
creazione - in corrispondenza alla dichiarazione - e
non può essere mai essere variata.
 2000 Prentice Hall, Inc. All rights reserved.
Array monodimensionali - dichiarazione
Intuitivamente un array monodimensionale - vettore - può essere utilizzato come un
contenitore suddiviso in elementi, ciascuno dei quali accessibile in modo indipendente.
Ogni elemento contiene un unico dato ed è individuato mediante un indice: l'indice del
primo elemento dell'array è 0, l''ultimo elemento di un array di N elementi ha indice N-1.
Il numero complessivo degli elementi dell'array viene detto dimensione, e nell'esempio
utilizzato è pari a N.
Per riassumere, un array è una struttura di dati composta da un numero determinato
di elementi dello stesso tipo, ai quali si accede singolarmente mediante un indice che
ne individua la posizione all'interno dell'array.
Per ogni array, così come per ogni variabile semplice (o non strutturata) è necessario
definire il tipo di dati; inoltre è necessario specificarne la dimensione, ossia il numero di
elementi che lo compongono. Una dichiarazione valida è la seguente:
int numeri[6];
Che corrisponde a:
[numeri[0] numeri[1] numeri[2] numeri[3] numeri[4] numeri[5]]
Viene indicato, come sempre, prima il tipo della variabile (int) poi il nome (numeri) ed
infine tra parentesi quadre la dimensione (6): l'array consente di memorizzare 6 numeri
interi.
Tra parentesi quadre è necessario indicare sempre un'espressione che sia un valore intero
costante. In base a quanto detto, è errato scrivere:
int numeri[];
int i, numeri[i];
 2000 Prentice Hall, Inc. All rights reserved.
Array (cont.)
• Array
– Gruppi di locazioni di memoria consecutive
– Tutte con lo stesso nome e tipo
• Per fare riferimento ad un elemento bisogna
conoscere
– Il nome dell’array
– Il numero che ne individua la posizione al
suo interno
• Formato: nomearray[numeroposizione]
– Il primo elemento ha indice 0
– Un array di n elementi di nome c é:
c[0], c[1]...c[n-1]
 2000 Prentice Hall, Inc. All rights reserved.
Nome dell’array (Tutti
gli elementi dell’array
hanno lo stesso nome,c)
c[0]
-45
c[1]
6
c[2]
0
c[3]
72
c[4]
1543
c[5]
-89
c[6]
0
c[7]
62
c[8]
-3
c[9]
1
c[10]
6453
c[11]
78
Numero che indica la
posizione del singolo
elemento all’interno dell’
array c
Dichiarare gli array
• Per dichiarare un array, si deve specificare
– Nome
– Tipo
– Numero di elementi
arrayType arrayName[ numberOfElements ];
int c[10];
float myArray[3284];
• Si possono dichiarare più array dello stesso tipo nella stessa istruzione.
– Il formato è analogo a quello delle variabili monodimensionali:
int b[100], x[27];
Il vincolo di dover specificare in fase di dichiarazione la dimensione dell'array porta
spesso ad un sovradimensionamento dell'array, al fine di evitare di non disporre dello
spazio necessario durante l'escuzione del programma.
Nel caso in cui sia dunque richiesto l'utilizzo di un array, la specifica dell'algoritmo
dovrà quindi prevedere o l'esatto numero di dati da gestire oppure un valore
massimo di dati.
 2000 Prentice Hall, Inc. All rights reserved.
Per accedere al singolo elemento dell'array è necessario indicare il nome dell'array e
l'indice dell'elemento posto tra parentesi quadre, ad esempio, per accedere al primo
elemento si dovrà scrivere:
numeri[0]
Si noti che gli elementi dell'array vanno dall'elemento di indice 0 a quello di indice 5.
Accedere all'elemento di indice 6 (o superiore) non sempre causa un errore di sintassi.
In generale il singolo elemento di un array può essere utilizzato come una semplice
variabile. Ad es:
c[0] = 3;
printf( "%d", c[0] );
Spesso l'array viene utilizzato all'interno di iterazioni per accedere uno dopo l'altro ai suoi
elementi, utilizzando un indice che viene modificato ad ogni iterazione. Ad esempio:
/* Inizializzazione di un array di numeri interi al valore nullo */
for (i = 0; i < 6; i++)
numeri[i] = 0;
L'indice i inizializzato a zero consente di accedere dal principio al primo elemento
dell'array e di proseguire fino all'ultimo, con indice 5 (si noti che quando l'indice è pari a 6
la condizione è falsa ed il corpo del ciclo non viene eseguito).
 2000 Prentice Hall, Inc. All rights reserved.
Esempi di utilizzo di array
• Initializzatori
int n[5] = {1, 2, 3, 4, 5 };
– Se non ci sono sufficienti inizializzatori, gli elementi più a destra
sono inizializzati a 0
– Se ce ne sono troppi, viene segnalato un errore di sintassi
int n[5] = {0}
• Pone tutti gli elementi uguali a 0
– Il C non ha un controllo sui limiti degli array
• Se la dimensione del vettore è omessa, gli
inizializzatori consentono di definirla in base al
loro numero
int n[] = { 1, 2, 3, 4, 5 };
– 5 inizializzatori, quindi n è un array di 5 elementi
 2000 Prentice Hall, Inc. All rights reserved.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*PROGRAMMA PER LA STAMPA DI UN ISTOGRAMMA*/
Outline
#include <stdio.h>
#define SIZE 10
int main()
{
int n[ SIZE ] = { 19, 3, 15, 7, 11, 9, 13, 5, 17, 1 };
int i, j;
Inizializzazione di array
printf( "%s%13s%17s\n", "Element", "Value", "Histogram" );
Cicli for annidati
for ( i = 0; i <= SIZE - 1; i++ ) {
printf( "%7d%13d
", i, n[ i ]) ;
for ( j = 1; j <= n[ i ]; j++ )
printf( "%c", '*' );
/* print one bar */
printf( "\n" );
}
Stampa risultati
return 0;
}
Element
Value
Histogram
0
19
*******************
1
3
***
2
15
***************
3
7
*******
4
11
***********
5
9
*********
6
13
*************
7
5
*****
8
17
*****************
9 Prentice Hall, Inc.
1 All rights *
 2000
reserved.
Output
/* Memorizza in un array di interi i voti
ottenuti da sei studenti e ne determina il
maggiore, il minore e la media */
#include <stdio.h>
main()
{
int voti[6];
int i, max, min;
float media;
/*array di 6 elementi interi*/
printf("VOTI STUDENTI\n\n");
/* Immissione voti nell’array (da 0 a 5)*/
for(i=0; i<=5; i++) {
printf("Voto studente n. %d: ", i+1);
scanf("%d", &voti[i]);
}
/* Ricerca del maggiore */
max = voti[0];
for(i=1; i<=5; i++)
if(voti[i]>max)
max = voti[i];
 2000 Prentice Hall, Inc. All rights reserved.
/* Ricerca del minore */
min = voti[0];
for(i=1; i<=5; i++)
if(voti[i]<min)
min = voti[i];
/* Calcolo della media */
media = voti[0];
for(i=1; i<=5; i++)
media = media + voti[i];
media = media/6;
printf("Maggiore: %d\n", max);
printf("Minore: %d\n", min);
printf("Media: %f\n", media);
}
/* legge i punteggi di n concorrenti su due prove
Determina la classifica
*/
for (i=0; i<n; i++) {
printf("\nConcorrente n.%d \n", i+1);
do {
#include <stdio.h>
printf("Prima prova: ");
#define MAX_CONC 1000 /* massimo numero di
concorrenti */
#define MIN_PUN 1
/* punteggio minimo per
ogni prova */
#define MAX_PUN 10 /* punteggio massimo per
ogni prova */
scanf("%f", &prova1[i]);
}
while (prova1[i]<MIN_PUN || prova1[i]>MAX_PUN);
do {
main()
{
/*prova1, prova2, totale sono vettori di dim. 1000*/
float prova1[MAX_CONC], prova2[MAX_CONC],
totale[MAX_CONC];
printf("Seconda prova: ");
scanf("%f", &prova2[i]);
}
while (prova2[i]<MIN_PUN || prova2[i]>MAX_PUN);
int i, n;
/*Numero di concorrenti n: >1 e <=1000*/
do {
printf("\nNumero concorrenti: ");
scanf("%d", &n);
}
while (n<1 || n>MAX_CONC);
/* Per ogni concorrente, si richiede il punteggio nelle
due prove */
}
/* Calcolo media per concorrente */
for (i=0; i<n; i++)
totale[i] = (prova1[i]+prova2[i])/2;
printf("\n
for (i=0; i<n; i++)
printf("%f %f %f \n", prova1[i], prova2[i], totale[i]);
}
 2000 Prentice Hall, Inc. All rights reserved.
CLASSIFICA\n");
Array di caratteri
•
Array di caratteri
– La stringa "hello" è un array di caratteri statico
– Gli array di caratteri si possono inizializzare usando stringhe di caratteri:
char string1[] = "first";
• Il carattere nullo '\0' termina la stringa
• string1 ha 6 elementi
char string1[] = { 'f', 'i', 'r', 's', 't', '\0' };
– string1[ 3 ] è il carattere 's'
– Il nome dell’array è anche il suo indirizzo in memoria, quindi per gli
array il simbolo & non è necessario in scanf
scanf( "%s", string2 ) ;
• Legge i caratteri finchè non trova il carattere nullo
 2000 Prentice Hall, Inc. All rights reserved.
#include <stdio.h>
#define DIM_INT 16
/*conversione decimale-binario */
void stampaBin ( int ); /*prototipo di funzione*/
main()
{
char resp[2];
int num;
resp[0] = 's';
void stampaBin( int v )
{
int i, j;
char a[DIM_INT];
/*DIM_INT è globale*/
/*resp è un array*/
if (v == 0)
printf("%d", v);
else {
while( resp[0] == 's' ) {
printf("\nInserisci un intero positivo: ");
scanf("%d", &num);
printf("La sua rappresentazione binaria e': ");
for( i=0; v != 0; i++) {
a[i] = v % 2;
v /= 2;
stampaBin( num ); /*chiamata della
funzione*/
}
for(j = i-1 ; j >= 0; j--)
printf("\nVuoi continuare? (s/n): ");
scanf("%s",resp);
printf("%d", a[j]);
}
}
}
 2000 Prentice Hall, Inc. All rights reserved.
}
1 /* array di caratteri*/
Outline
2
3 #include <stdio.h>
Inizializza le stringhe
4
5 int main()
6 {
7
char string1[ 20 ], string2[] = "string literal";
8
int i;
Input della stringa
string1
9
10
printf(" Enter a string: ");
11
scanf( "%s", string1 );
12
printf( "string1 is: %s\nstring2: is %s\n"
13
"string1 with spaces between characters is:\n",
14
string1, string2 );
15
16
17
for ( i = 0; string1[ i ] != '\0'; i++ )
printf( "%c ", string1[ i ] );
18
19
printf( "\n" );
20
return 0;
Stampa le stringhe
Ciclo for che stampa I
singoli caratteri della
stringa 1: usa il
formato %c
21 }
Enter a string: Hello
string1 is: Hello
string2 is: string literal
string1 with spaces between characters is:
H el2000
l oPrentice Hall, Inc. All rights reserved.
Program Output
Array bidimensionali
Gli array bidimensionali sono organizzati per righe e per
colonne, come matrici.
La specifica di un array bidimensionale prevede l'indicazione
del tipo di dati contenuti nell'array, del nome e delle due
dimensioni, cioè numero di righe e numero di colonne,
racchiusa ciascuna tra parentesi quadre. Ad es, a[3][4] indica
un array bidimensionale di 3 righe e 4 colonne:
Column 0
Column 1
Column 2
Column 3
Row 0
a[0][0]
a[0][1]
a[0][2]
a[0][3]
Row 1
a[1][0]
a[1][1]
a[1][2]
a[1][3]
Row 2
a[2][0]
a[2][1]
a[2][2]
a[2][3]
 2000 Prentice Hall, Inc. All rights reserved.
La dichiarazione che segue specifica un array bidimensionale di numeri
reali, organizzati su 4 righe e 6 colonne, per un totale di 24 elementi:
float livelli[4][6];
Come esempio di scansione degli elementi di un array bidimensionale si
consideri lo stralcio di codice qui riportato, in cui si accede riga per riga ad
ogni elemento dell'array mat, mediante due cicli annidati. Il ciclo più esterno
scandisce le righe, quello più interno le colonne.
/* Acquisizione da tastiera dei valori della matrice mat[4][6] */
for (i = 0; i < 4; i++)
for(j = 0; j < 6; j++)
{
printf("Inserisci l'elemento riga %d colonna %d: ", i, j);
scanf("%f", &mat[i][j]);
}
Gli elementi vengono memorizzati per righe, quindi è più veloce accedere
per righe ai dati memorizzati.
 2000 Prentice Hall, Inc. All rights reserved.
Array bidimensionali (cont.)
• Inizializzazione
int b[ 2 ][ 2 ] = { { 1, 2 }, { 3, 4 } };
– Gli inizializzatori sono raggruppati per righe in
parentesi fraffe
– Gli elementi non specificati sono posti uguali a zero
int b[ 2 ][ 2 ] = { { 1 }, { 3, 4 } };
• Riferimento ai singoli elementi
– Si deve specificare prima la riga e poi la colonna
printf( "%d", b[ 0 ][ 1 ] );
 2000 Prentice Hall, Inc. All rights reserved.
1
2
3
4
1
0
3
4
/* Caricamento di una matrice 4x3 */
#include <stdio.h>
int mat[4][3];
main()
{
int i, j;
printf("\n \n CARICAMENTO DELLA MATRICE \n \n");
for(i=0; i<4; i++)
for(j=0; j<3; j++) {
printf("Inserisci linea %d colonna %d val: ", i, j);
scanf("%d", &mat[i][j]);
};
/* Visualizzazione */
for(i=0; i<4; i++) {
printf("\n");
for(j=0; j<3; j++)
printf("%5d", mat[i][j]);
}
}
 2000 Prentice Hall, Inc. All rights reserved.
/* Caricamento di una matrice
le cui dimensioni <100x100 vengono decise
dall'utente */
#include <stdio.h>
#define MAXLINEE 100
#define MAXCOLONNE 100
int mat[MAXLINEE][MAXCOLONNE];
main()
{
int n, m;
int i, j;
while((m>=MAXCOLONNE) || (m<1));
printf("\n \n CARICAMENTO DELLA
MATRICE \n \n");
for(i=0; i<n; i++)
for(j=0; j<m; j++) {
printf("Inserisci linea %d colonna %d
val:", i, j);
scanf("%d", &mat[i][j]);
};
/* Richiesta delle dimensioni */
do {
printf("\nNumero di linee: ");
scanf("%d", &n);
}
while((n>=MAXLINEE) || (n<1));
do {
printf("Numero di colonne: ");
scanf("%d", &m);
}
/* Visualizzazione */
for(i=0; i<n; i++) {
printf("\n");
for(j=0; j<m; j++)
printf("%5d", mat[i][j]);
}
}
 2000 Prentice Hall, Inc. All rights reserved.
/* Calcolo del prodotto righe x colonne di due
matrici */
#include <stdio.h>
#define N 4
#define P 3
#define M 5
int mat1[N][P];
/* prima matrice */
int mat2[P][M];
/* seconda matrice */
int pmat[N][M];
/* matrice prodotto */
main()
{
int i, j, k;
printf("\n \n CARICAMENTO DELLA PRIMA
MATRICE \n \n");
for(i=0; i<N; i++)
for(j=0; j<P; j++) {
printf("Inserisci linea %d colonna %d val:", i, j);
scanf("%d", &mat1[i][j]);
};
printf("\n \n CARICAMENTO DELLA
SECONDA MATRICE \n \n");
for(i=0; i<P; i++)
for(j=0; j<M; j++) {
printf("Inserisci linea %d colonna %d val:", i, j);
scanf("%d", &mat2[i][j]);
};
for(i=0; i<N; i++)
for(j=0; j<M; j++) {
pmat[i][j] = 0;
for(k=0; k<P; k++)
pmat[i][j] = pmat[i][j] + mat1[i][k] * mat2[k][j];
};
printf("\n \n PRIMA MATRICE \n ");
for(i=0; i<N; i++) {
printf("\n");
for(j=0; j<P; j++)
printf("%5d", mat1[i][j]);
}
printf("\n \n SECONDA MATRICE \n ");
for(i=0; i<P; i++) {
printf("\n");
for(j=0; j<M; j++)
printf("%5d", mat2[i][j]);
}
printf("\n \n MATRICE PRODOTTO \n ");
for(i=0; i<N; i++) {
printf("\n");
for(j=0; j<M; j++)
printf("%5d", pmat[i][j]);
/* Calcolo del prodotto */
}
 2000 Prentice Hall, Inc. All rights reserved.
}
Chiamata di funzioni
Passaggio parametri per valore e per indirizzo
Passaggio per valore
– Si effettua una copia degli argomenti (parametri) passati alla funzione
– Le modifiche all’interno della funzione non alterano i valori originali
– Si usa quando non è richiesta la modifica dei parametri
• E’ utile per evitare modifiche accidentali
Passaggio per indirizzo
– Vengono passati gli argomenti originali
– Le modifiche all’interno della funzione hanno effetto sui valori originali
Per ora abbiamo parlato solo del passaggio parametri per
valore
 2000 Prentice Hall, Inc. All rights reserved.
Passaggio di array a funzioni
• Passaggio di array
– Si passa il nome dell’array senza parentesi quadre
int myArray[ 24 ];
myFunction( myArray, 24 );
• E’ bene in genere passare anche la dimensione dell’array
– Gli array sono passati per indirizzo
– Il nome dell’array è l’indirizzo del suo primo elemento
– La funzione “sa” dove è memorizzato l’array
• Può quindi effettuare modifiche sugli elementi dell’array originale
• Passaggio di elementi di un array
– Il passaggio avviene per valore (copia dell’originale)
– Si passa il nome e la posizione fra parentesi (myArray[3])
 2000 Prentice Hall, Inc. All rights reserved.
Passaggio di array a funzioni (cont.)
Il passaggio di array a sottoprogrammi viene sempre fatto per indirizzo o
riferimento, passando per valore l'indirizzo dell'array, e lasciando quindi
di fatto accessibile al sottoprogramma l'array con la possibilità di
modificarne il contenuto. Per questo motivo è necessario prestare estrema
attenzione durante l'accesso agli elementi dell'array.
Ogniqualvolta si passa un array pluridimensionale è necessario indicare tra parentesi quadre - tutte le dimensioni dell'array ad eccezione della
prima. Ne consegue che per un array monodimensionale la dichiarazione
dell'array come parametro viene fatta così:
void funzionex(int numeri[], ...)
{
...
}
Nel caso di array pluridimensionali, o più comunemente i bidimensionali,
la dichiarazione deve essere fatta come segue:
void funzioney(int livelli[][6], ...)
{
...
}
 2000 Prentice Hall, Inc. All rights reserved.
E’ buona norma, passare sempre, mediante ulteriori parametri, il numero di elementi
dell'array monodimensionale, o il numero di righe e di colonne in array
bidimensionali, come mostrato nei seguenti stralci di codice:
void funzionex(int numeri[], int num_elem)
{
...
}
void funzioney(int livelli[][6], int num_righe, int num_colonne)
{
...
}
Questo rende più generale l’uso della funzione. Infatti, ad un sottoprogramma
potrebbe venire passata solo una porzione ridotta dell'intero array.
Prototipo della funzione
void modifyArray( int b[], int arraySize );
– Nei prototipi i nomi dei parametri sono opzionali, cioè
• int b[] può essere semplicemente int []
• int arraySize può essere semplicemente int
 2000 Prentice Hall, Inc. All rights reserved.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
Passaggio di array e di elementi di array a funzioni */
#include <stdio.h>
#define SIZE 5
Outline
Dichiarazione delle
funzioni
void modifyArray( int [], int );
void modifyElement( int );
int main()
{
int a[ SIZE ] = { 0, 1, 2, 3, 4 }, i;
Inizializzazione di a
printf( "Effects of passing entire array call "
"by reference:\n\nThe values of the "
"original array are:\n" );
for ( i = 0; i <= SIZE - 1; i++ )
printf( "%3d", a[ i ] );
L’array è passato per indirizzo e
può essere modificato
printf( "\n" );
modifyArray( a, SIZE ); /* passaggio di array per indirizzo */
printf( "The values of the modified array are:\n" );
for ( i = 0; i <= SIZE - 1; i++ )
printf( "%3d", a[ i ] );
Passaggio di array alla
funzione (per indirizzo)
L’elemento dell’array è passato per
valore e non può essere modificato
printf( "\n\n\nEffects of passing array element call "
"by value:\n\nThe value of a[3] is %d\n", a[ 3 ] );
modifyElement( a[ 3 ] );
printf( "The value of a[ 3 ] is %d\n", a[ 3 ] );
return 0;

2000
Prentice Hall, Inc. All rights reserved.
}
Passaggio di un
elemento dell’array alla
funzione (per valore)
33
34 void modifyArray( int b[], int size )
Outline
35 {
36
int j;
Definizione delle
funzioni
37
38
for ( j = 0; j <= size - 1; j++ )
39
b[ j ] *= 2;
40 }
41
42 void modifyElement( int e )
43 {
44
printf( "Value in modifyElement is %d\n", e *= 2 );
45 }
Effects of passing entire array call by reference:
The values of
0 1 2 3
The values of
0 2 4 6
the original array are:
4
the modified array are:
8
Effects of passing array element call by value:
The value of a[3] is 6
Value in modifyElement is 12
The value of a[3] is 6
 2000 Prentice Hall, Inc. All rights reserved.
Output del programma
Outline
/* ESEMPIO DI ARRAY BIDIMENSIONALE */
#include <stdio.h>
#define STUDENTS 3
#define EXAMS 4
int minimum(int [][ EXAMS ], int, int );
int maximum(int [][ EXAMS ], int, int );
double average(int [], int );
void printArray(int [][ EXAMS ], int, int );
int main()
{
int student;
int studentGrades[ STUDENTS ][ EXAMS ] =
{ { 77, 68, 86, 73 },
{ 96, 87, 89, 78 },
{ 70, 90, 86, 81 } };
Definizione delle
funzioni
Ogni riga è uno studente,
ogni colonna è il voto per
ciascun esame
printf( "The array is:\n" );
printArray( studentGrades, STUDENTS, EXAMS );
printf( "\n\nLowest grade: %d\nHighest grade: %d\n",
minimum( studentGrades, STUDENTS, EXAMS ),
maximum( studentGrades, STUDENTS, EXAMS ) );
for ( student = 0; student <= STUDENTS - 1; student++ )
printf( "The average grade for student %d is %.2f\n",
student,
average( studentGrades[ student ], EXAMS ) );
}
return 0;
 2000 Prentice Hall, Inc. All rights reserved.
Inizializzazione della
matrice studentGrades
Chiamata della
funzione printArray
Chiamate delle
funzioni
maximum,minimum,av
erage
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
/* Ricerca del voto minimo */
int minimum(int grades[][ EXAMS ],
int pupils, int tests )
{
int i, j, lowGrade = 100;
Outline
Definizione della
funzione minimum
for ( i = 0; i <= pupils - 1; i++ )
for ( j = 0; j <= tests - 1; j++ )
if ( grades[ i ][ j ] < lowGrade )
lowGrade = grades[ i ][ j ];
return lowGrade;
}
/* Ricerca del voto massimo */
int maximum(int grades[][ EXAMS ],
int pupils, int tests )
{
int i, j, highGrade = 0;
Definizione della
funzione maximum
for ( i = 0; i <= pupils - 1; i++ )
for ( j = 0; j <= tests - 1; j++ )
if ( grades[ i ][ j ] > highGrade )
highGrade = grades[ i ][ j ];
return highGrade;
}
/* Determine the average grade for a particular exam */
double average(int setOfGrades[], int tests )

{ 2000 Prentice Hall, Inc. All rights reserved.
Calcolo del voto medio
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
int i, total = 0;
Outline
for ( i = 0; i <= tests - 1; i++ )
total += setOfGrades[ i ];
return total / tests;
}
/* Print the array */
void printArray(int grades[][ EXAMS ],
int pupils, int tests )
{
int i, j;
printf( "
[0]
[1]
[2]
Definizione della
funzione printArray
[3]" );
for ( i = 0; i <= pupils - 1; i++ ) {
printf( "\nstudentGrades[%d] ", i );
for ( j = 0; j <= tests - 1; j++ )
printf( "%-5d", grades[ i ][ j ] );
}
}
 2000 Prentice Hall, Inc. All rights reserved.
Allinea a sinistra in
un campo di 5 cifre
Outline
The array is:
[0]
studentGrades[0] 77
studentGrades[1] 96
studentGrades[2] 70
[1]
68
87
90
[2]
86
89
86
[3]
73
78
81
Lowest grade: 68
Highest grade: 96
The average grade for student 0 is 76.00
The average grade for student 1 is 87.50
The average grade for student 2 is 81.75
 2000 Prentice Hall, Inc. All rights reserved.
Program Output