Funzioni in C
Servitore:• Un qualunque ente computazionale capace di nascondere la propria organizzazione interna
• presentando ai clienti una precisa interfaccia per lo scambio di informazioni
Cliente:• qualunque ente in grado di invocare uno o più servitori per
svolgere il proprio compito
Modello cliente/servitore
Modello cliente/servitoreUn servitore può• essere passivo o attivo
• servire molti clienti oppure costituire la risorsa privata diuno specifico cliente– in particolare: può servire un cliente alla volta, in
sequenza, oppure più clienti per volta, in parallelo
• trasformarsi a sua volta in cliente, invocando altriservitori o anche se stesso.
•Lo scambio di informazioni tra un cliente e un servitore può avvenire
in modo esplicito tramite le interfacce stabilite dal servitore
in modo implicito tramite aree-dati accessibili ad entrambi,
ossia l’ambiente condiviso
Comunicazione cliente/servitore
• Una funzione è un servitore– passivo– che serve un cliente per volta– che può trasformarsi in cliente invocando altre funzioni o se
stessa• Una funzione è un servitore dotato di nome che incapsula le
istruzioni che realizzano un certo servizio.• Il cliente chiede al servitore di svolgere il servizio
– chiamando tale servitore (per nome)– fornendogli le necessarie informazioni
• Nel caso di una funzione, cliente e servitore comunicanomediante l’interfaccia della funzione.
FUNZIONI COME SERVITORI
•L’interfaccia (o prototipo) di una funzione comprende
nome della funzionelista dei parametritipo del valore da essa denotato
• Esplicita il contratto di servizio fra cliente e servitore
•Cliente e servitore comunicano quindi mediantei parametri trasmessi dal cliente al servitore all’atto della chiamatail valore restituito dal servitore al cliente
Interfaccia di una funzione
• Calcolo del massimo di due valori
• Sequenza operazioni:– Il cliente comunica al
servitore i due valori– Il servitore calcola il massimo– Il servitore comunica al
cliente il valore del massimo
•Cliente e servitore devono essere d’accordo su:–il tipo dei valori int–quanti sono i valori 2–qual’è il nome del servitore max–qual’è il tipo del valore di ritorno int
•Il cliente non è interessato all’algoritmo che il servitore utilizza
5
3,5
ESEMPIO
ESEMPIO
• Quindi, dal lato del servitore dovrà esserci scritto:
– come si chiama– di quanti dati ha bisogno e di
che tipo– di che tipo è il valore calcolato
• Il cliente dovrà mandare alservitore identificato da quelnome– i dati di ingresso, in numero
e tipo giusti– ricevere il risultato
int max (int x, int y ){ /*interfacciaif (x>y) return x ;else return y;}
• Il simbolo max denota il nome della funzione
• Le variabili intere x e y sono i parametri della funzione
• Il valore restituito è un intero int
Funzioni: esempio C
• Il cliente passa informazioni al servitore mediante una serie di parametri
Parametri formali:sono specificati nella dichiarazione del servitoreesplicitano il contratto fra servitore e clienteindicano che cosa il servitore si aspetta dal cliente
Parametri attuali:sono trasmessi dal cliente all’atto della chiamatadevono corrispondere ai parametri formali in numero, posizione e tipo
Comunicazione cliente/servitore
Funzioni: esempio parametri
Legame tra parametri attuali e parametri formali:• effettuato al momento della chiamata, in modo
dinamico
Tale legame:vale solo per l’invocazione correntevale solo per la durata della funzione
Comunicazione cliente/servitore
Esempio
All’atto di questa chiamata della funzione, si effettua un legame tra:x e zy e 4
Esempio:
All’atto della chiamata della funzione si effettua il legame tra:x e 5y e z
Information hiding• La struttura interna (corpo) di una funzione è
completamente inaccessibile dall’esterno
• Così facendo si garantisce protezione dell’informazione(information hiding)
• Una funzione è accessibile solo attraverso la suainterfaccia
• Quindi posso cambiare l’algoritmo della funzione senza preoccuparmi di quello che succede dal lato del cliente
Definizione di funzione in C
<tipoValore> <nome>(<parametri-formali>) {
<corpo>}
<parametri-formali>o una lista vuota: voido una lista di variabili (separate da virgole)
visibili solo entro il corpo della funzione
<tipoValore>deve coincidere con il tipo del valore restituito dalla funzionePuò non esservi valore restituiti, in tal caso il tipo è void
La forma base è:return <espressione>;
Definizione di funzione in C
<tipoValore> <nome>(<parametri-formali>) {
<corpo>}
La forma base è:return <espressione>;
• Nella parte corpo possono essere presenti: definizioni e/o dichiarazioni locali (parte dichiarazioni) e un insieme di istruzioni (parte istruzioni)
• I dati riferiti nel corpo possono essere costanti, variabili, oppure parametri formali
• All'interno del corpo, i parametri formali vengono trattati come variabili
Funzioni: nascita e morte
• All’atto della chiamata:l’esecuzione del cliente viene sospesa e ilcontrollo passa al servitore
• Il servitore “vive” solo per il tempo necessario a svolgere il servizio
• Al termine, il servitore “muore”, e l’esecuzione torna al cliente
Chiamata di funzione
• La chiamata di funzione è un’espressione della forma
<nomefunzione> ( <parametri-attuali> )
• dove:
<parametri-attuali> ::=[ <espressione> ] { , <espressione> }
Funzioni: esempioFunzioni: esempio
int max (int x, int y ){if (x>y) return x ;
else return y;}
SERVITORE
Definizione della funzione
CLIENTE
Chiamata della funzione
main() {int z = 8;int m;m = max ( z, 4);
}
Parametri formali
Parametri attuali
Risultato di una funzione
• L’istruzione return provoca la restituzione del controllo al cliente, unitamente al valore della espressione che la segue.
• Eventuali istruzioni successive alla return non saranno mai eseguite
int max (int x, int y ){if (x>y) return x ;else return y;
}
Funzioni: esempioFunzioni: esempio
int max (int x, int y ){if (x>y) return x ;
else return y;}
SERVITORE
Definizione della funzione
CLIENTE
Chiamata della funzione
main() {int z = 8;int m;m = max ( z, 4);
}
Parametri formali
Risultato
Riassumendo
All’atto dell’invocazione di una funzione:
• si crea una nuova attivazione (istanza) del servitore
• si alloca la memoria per i parametri (e le eventuali variabili locali)
• si trasferiscono i parametri al servitore
• si trasferisce il controllo al servitore
• si esegue il codice della funzione
Passaggio dei parametri
In generale, un parametro può essere trasferito dal cliente al servitore:
• per valore o copia (by value)si trasferisce il valore del parametro attuale
• per riferimento (by reference)si trasferisce un riferimento al parametro attuale
Passaggio per valore
• Si trasferisce una copia del valore del parametro attuale
cliente
Passaggio per valore
• Si trasferisce una copia del valore del parametro attuale
cliente servitore
Istanza del servitore
Ogni azionefatta su w èlocale al servitore
Valore (copiato) di z
copia
Passaggio per riferimento
• Si trasferisce un riferimento al parametro attuale
cliente
Passaggio per valore
• Si trasferisce un riferimento al parametro attuale
cliente servitore
Istanza del servitore
Ogni azionefatta su w èIn realtàfatta sulla variabile z del cliente
Riferimento a z(indirizzo)
riferimento
xx
Passaggio dei parametri in C
• In C, i parametri sono trasferiti sempre e solo per valore
si trasferisce una copia del parametro attuale, non l’originale
tale copia è strettamente privata e locale a quel servitore
il servitore potrebbe quindi alterare il valore ricevuto, senza che ciò abbia alcun impatto sul cliente
Passaggio dei parametri in C
• In C, i parametri sono trasferiti sempre e solo per valore
Conseguenza:
• è impossibile usare un parametro per trasferire informazioni verso il cliente
• per trasferire un’informazione al cliente si sfrutta il valore di ritorno della funzione
Perché il passaggio per valore non basta?
Problema: scrivere una procedura che scambi i valori di due variabili intere
Specifica: Dette A e B le due variabili, ci si può appoggiare a una variabile ausiliaria T, e svolgere lo scambio in tre fasi
Frammento di codice:int a,b,t;...t = a; a = b; b = t;…
Esempio
Esempio
• Supponendo di utilizzare, senza preoccuparsi, il passaggio per valore usato finora, la codifica potrebbe essere espressa come segue:
void scambia(int a, int b) {int t;t = a; a = b; b = t;return;
}
EsempioIl cliente invocherebbe quindi la procedura così:
main(){int y = 5, x = 33;scambia(x, y);/* ora dovrebbe essere
x=5, y=33 ...MA NON È VERO
*/}
Perché non funziona?
Esempio: cosa è successo?
• La procedura ha effettivamente scambiato i valori di A e B al suo interno
• ma questa modifica non si è propagata al cliente, perché sono state scambiate le copie locali alla procedura, non gli originali
• al termine della procedura, le sue variabili locali sono state distrutte, quindi nulla è rimasto del lavoro svolto dalla procedura
Esempio: cosa è successo?• Ogni azione fatta su a e b è strettamente locale al servitore.
Quindi a e b vengono scambiati, ma quando il servitore termina, tutto scompare
cliente servitore
Valori (copiati) di a e b
copia
x
y
Passaggio dei parametri in C
Il C adotta sempre il passaggio per valore!
È sicuro: le variabili del cliente e del servitore sono disaccoppiate
... ma non consente di scrivere componenti software il cui scoposia diverso dal calcolo di una espressione
Per superare questo limite occorre il passaggio per riferimento
Il passaggio per riferimento (by reference):
• non trasferisce una copia del valore del parametro attuale
• ma un riferimento al parametro, in modo da dare al servitore accesso diretto al parametro in possesso del cliente
• il servitore, quindi, accede direttamente al dato del cliente e può modificarlo
Passaggio per riferimento
Passaggio per riferimento
cliente
x
y
Si trasferisce un riferimento ai parametri attuali (cioè i loro indirizzi)
cliente servitore
Riferimenti a x e y (indirizzi)
riferimento
x
yriferimentoβ
α
Passaggio per riferimentoOgni azione fatta su a e b, in realtàè fatta su x e y nell’environment del
cliente
cliente servitore
Riferimenti a x e y (indirizzi)
riferimento
x
yriferimentoβ
α
Passaggio per riferimento
quindi, scambiando α e β, in realtà si scambiano x e y
Realizzare il passaggio per riferimento in C
• Il C non fornisce direttamente un modo per attivare il passaggio per riferimento -> a volte occorre costruirselo
• è una grave mancanza!
• il C lo fornisce indirettamente solo per alcuni tipi di dati
• quindi, occorre costruirselo quando serve.(vedremo più avanti dei casi)
Funzioni e strutture
• Con le funzioni si possono usare come parametri e come valore di ritorno le strutture (con gli array è un po’ diverso, come vedremo)
• L’utilizzo delle funzioni permette di costruire semplicemente applicazioni con la metodologia top-down
• Si scriva un programma che permette di– leggere due frazioni– calcolarne la somma– visualizzare il risultato
Esempio• Per prima cosa, definiamo le strutture dati:
– una frazione è costituita da un numeratore ed un denominatore
typedef struct { int num; int den; } frazione;
• Poi scriviamo l’algoritmo partendo dalla versione più astratta.Scriviamo il main invocando le varie funzioni che ci servono
main(){ frazione f1, f2, somma;
f1 = leggiFrazione();f2 = leggiFrazione();somma = sum(f1,f2);printf("%d/%d",somma.num,somma.den);
}
Esempio
• Poi implementiamo le funzioni che abbiamo invocato nel main:
frazione leggiFrazione(){ frazione f;
scanf("%d",&f.num);scanf("%d",&f.den);return f;
}
Esempio
• La somma di due frazioni si calcola così:– calcolo il minimo comun denominatore (minimo comune multiplo
dei denominatori); questo è il denominatore della somma– porto la prima frazione al comun denominatore– porto la seconda frazione al comun denominatore– calcolo la somma dei numeratori: questo è il numeratore della somma
frazione sum(frazione f1, frazione f2){ int mcd; // minimo comun denominatorefrazione somma;mcd = mcm(f1.den,f2.den);somma.den = mcd;f1 = portaDen(f1,mcd);f2 = portaDen(f2,mcd);somma.num = f1.num + f2.num;return somma;}
Esempio
• Infine implementiamo le funzioni che abbiamo usato nelle funzioni
• Per portare una frazione ad un denominatore, devo moltiplicarenumeratore e denominatore per la stessa quantità
nuovoNum/nuovoDen = vecchioNum/vecchioDen
• quindinuovoNum=vecchioNum*nuovoDen/vecchioDen
frazione portaDen(frazione f, int nDen){ frazione nuovo;
nuovo.den = nDen;nuovo.num = f.num*nDen/f.den;return nuovo;
}
Esempio
• Per calcolare il minimo comune multiplo di dueinteri, posso farne il prodotto e dividere per il massimo comun divisore dei due
int mcm(int a, int b){ return a*b/MCD(a,b);}
Esempio
• Per calcolare il MCD di due numeri, posso usare il metodo di Euclide
int MCD(int m, int n){ while (m != n)
if (m>n)m=m-n;
else n=n-m;return m;
}
#include <stdio.h>typedef struct { int num; int den; }
frazione;int MCD(int m, int n){ while (m != n)
if (m>n)m=m-n;
else n=n-m;return m;
}int mcm(int a, int b){ return a*b/MCD(a,b);}frazione portaDen(frazione f, int
nDen){ frazione nuovo;nuovo.den = nDen;nuovo.num = f.num*nDen/f.den;return nuovo;
}
frazione sum(frazione f1, frazione f2){ int mcd;
frazione somma;mcd = mcm(f1.den,f2.den);somma.den = mcd;f1 = portaDen(f1,mcd);f2 = portaDen(f2,mcd);somma.num = f1.num + f2.num;return somma;
}frazione leggiFrazione(){ frazione f;
scanf("%d",&f.num);scanf("%d",&f.den);return f;
}main(){ frazione f1, f2, somma;
f1 = leggiFrazione();f2 = leggiFrazione();somma = sum(f1,f2);printf("%d/%d",somma.num,somma.den);
}
Il programma risultante
Il programma risultante• E` abbastanza facile da scrivere e da capire• E` facile da modificare• Es: voglio assicurarmi che l’utente non inserisca una frazione che ha perdenominatore zero
• Intervengo in una sola funzione: la leggiFrazione– è una funzione di 4 istruzioni, quindi facile da capire e da
modificare
frazione leggiFrazione(){ frazione f;
do{ scanf("%d",&f.num);
scanf("%d",&f.den);if (f.den==0)
printf(“Re-inserire la frazione\n");} while (f.den == 0);
return f;}
Modificabilità
• Ora voglio che mi fornisca solo frazioni ai minimi termini
• Aggiungo una funzione riduci. Posso invocarla nel main
main(){ frazione f1, f2, somma;
f1 = leggiFrazione();f2 = leggiFrazione();somma = riduci(sum(f1,f2));printf("%d/%d",somma.num,somma.den);
}
Modificabilità
• Poi definisco la nuova funzione riduci
• Per ridurre una funzione ai minimi termini, basta dividere numeratore e denominatore per il loro MCD
frazione riduci(frazione f){ int m = MCD(f.num,f.den);
f.num = f.num/m;f.den = f.den/m;return f;
}
Top Related