Programmazione modulare in C
Programmazione modulare in C
Programmazione modulare in C
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
<strong>Programmazione</strong> <strong>modulare</strong> nel<br />
l<strong>in</strong>guaggio C<br />
Paolo Bernardi<br />
07EIPCH<br />
AA 2008/09<br />
Ing. Paolo Bernardi
Introduzione<br />
– Non è possibile creare programmi di elevata<br />
complessità lavorando con un unico file sorgente<br />
• Lentezza di ricompilazione<br />
• Difficile riuso di procedure<br />
• Impossibile collaborare tra più programmatori<br />
• Difficile tener traccia delle modifiche<br />
– Occorre realizzare programmi distribuiti su più file<br />
sorgenti<br />
– Chiameremo tali programmi progetti multi-file o,<br />
più semplicemente progetti.<br />
2<br />
Ing. Paolo Bernardi
Compilazione e L<strong>in</strong>k<br />
.c<br />
Sorgente<br />
C<br />
Sorgente<br />
C<br />
Sorgente<br />
C<br />
Sorgente<br />
Assembler<br />
Compilatore Compilatore Compilatore<br />
Assemblatore<br />
.obj<br />
Codice<br />
oggetto<br />
Codice<br />
oggetto<br />
Codice<br />
oggetto<br />
Codice<br />
oggetto<br />
.exe<br />
Codice<br />
eseguibile<br />
L<strong>in</strong>ker<br />
Libreria<br />
Libreria<br />
.lib<br />
3<br />
Ing. Paolo Bernardi
Struttura del compilatore<br />
Sorgente C<br />
Analisi lessicale<br />
Analisi s<strong>in</strong>tattica<br />
Analisi semantica<br />
Generazione codice<br />
<strong>in</strong>dipendente HW<br />
Ottimizzazione<br />
Generazione codice b<strong>in</strong>ario<br />
Ottimizzazione<br />
Codice<br />
oggetto<br />
4<br />
Ing. Paolo Bernardi
Modulo di Codice oggetto<br />
– Contiene il codice b<strong>in</strong>ario corrispondente ad un<br />
modulo C<br />
– Contiene dei riferimenti esterni a funzioni e<br />
variabili dichiarate <strong>in</strong> altri moduli<br />
– Contiene funzioni e variabili utilizzabili da altri<br />
moduli<br />
– Uno ed un solo modulo nell’<strong>in</strong>tero progetto<br />
contiene il programma o funzione pr<strong>in</strong>cipale<br />
• <strong>in</strong>t ma<strong>in</strong>(void){…};<br />
5<br />
Ing. Paolo Bernardi
Modulo oggetto<br />
Funzioni usate dal<br />
modulo ma def<strong>in</strong>ite<br />
altrove<br />
strcmp<br />
strcpy<br />
6<br />
stdout<br />
Variabili usate dal<br />
modulo ma def<strong>in</strong>ite<br />
altrove<br />
sort.c<br />
sort.obj<br />
Codice<br />
b<strong>in</strong>ario<br />
Funzioni def<strong>in</strong>ite nel<br />
modulo, richiamabili<br />
anche dall’esterno<br />
<strong>in</strong>ssort<br />
cntsort<br />
scambi<br />
Variabili def<strong>in</strong>ite nel<br />
modulo, utilizzabili<br />
anche dall’esterno<br />
Ing. Paolo Bernardi
L<strong>in</strong>ker<br />
Codice<br />
oggetto<br />
Codice<br />
oggetto<br />
Codice<br />
oggetto<br />
Codice<br />
oggetto<br />
Codice<br />
oggetto<br />
7<br />
Ing. Paolo Bernardi
Modularità<br />
• Ciascun modulo <strong>in</strong> C:<br />
– Def<strong>in</strong>isce alcune funzioni “private”<br />
– Def<strong>in</strong>isce alcune funzioni “pubbliche”<br />
– Def<strong>in</strong>isce alcune variabili “private”<br />
– Def<strong>in</strong>isce alcune variabili “pubbliche”<br />
– Chiama alcune funzioni “esterne”<br />
– Usa alcune variabili “esterne”.<br />
8<br />
Ing. Paolo Bernardi
Chiamata di funzioni (I)<br />
void InsertionSort(<strong>in</strong>t A[], <strong>in</strong>t n) ;<br />
Funzione<br />
pubblica<br />
void InsertionSort(<strong>in</strong>t A[], <strong>in</strong>t n)<br />
{<br />
<strong>in</strong>t i, j, key ;<br />
/* … */<br />
libreria.c<br />
9<br />
extern void InsertionSort(<strong>in</strong>t A[], <strong>in</strong>t n) ;<br />
/* … */<br />
<strong>in</strong>t ma<strong>in</strong>(void)<br />
{ <strong>in</strong>t v[10] ;<br />
/* … */<br />
InsertionSort(v, 10) ;<br />
client.c<br />
/* … */<br />
Ing. Paolo Bernardi
Chiamata di funzioni (II)<br />
– Il modulo che esporta una funzione non deve fare<br />
nulla di particolare<br />
• <strong>in</strong> C, tutte le funzioni sono pubbliche cioè hanno classe<br />
di memorizzazione automatica<br />
• tranne nei casi <strong>in</strong> cui la loro dichiarazione sia preceduta<br />
dalla parola chiave static (funzioni private)<br />
– <strong>in</strong>vocabili esclusivamente dal modulo che le def<strong>in</strong>isce<br />
– Il modulo che utilizza la funzione deve dichiararne<br />
il prototipo (con la parola chiave extern)<br />
– Il l<strong>in</strong>ker farà il collegamento.<br />
10<br />
Ing. Paolo Bernardi
Funzioni private<br />
– Nel caso <strong>in</strong> cui un modulo def<strong>in</strong>isca delle funzioni<br />
che NON vuole esportare, nella def<strong>in</strong>izione (non<br />
nel prototipo) deve comparire la parola chiave<br />
static.<br />
void InsertionSort(<strong>in</strong>t A[], <strong>in</strong>t n) ;<br />
void CancellaVettore(<strong>in</strong>t A[], <strong>in</strong>t n) ;<br />
void InsertionSort(<strong>in</strong>t A[], <strong>in</strong>t n)<br />
{ . . . }<br />
static void CancellaVettore(<strong>in</strong>t A[], <strong>in</strong>t n)<br />
{ . . . }<br />
11<br />
Ing. Paolo Bernardi
Variabili globali (I)<br />
<strong>in</strong>t n_elem ; client.c<br />
double vett[MAX] ;<br />
static <strong>in</strong>t temp ;<br />
extern <strong>in</strong>t n_elem ; libreria.c<br />
extern double vett[MAX] ;<br />
/* … */<br />
{ for(i=0; i
Variabili globali (II)<br />
– Il modulo che esporta una variabile globale non<br />
deve fare nulla di particolare: <strong>in</strong> C, tutte le variabili<br />
globali sono pubbliche (per default)<br />
– Il modulo che utilizza la variabile deve ri-def<strong>in</strong>irla<br />
con la parola chiave extern<br />
– Se non si vuole esportare una variabile occorre<br />
def<strong>in</strong>irla con la parola chiave static<br />
– Le variabili locali (alle procedure) non possono<br />
essere condivise.<br />
13<br />
Ing. Paolo Bernardi
Problemi<br />
– Le due dichiarazioni (nel modulo che esporta e<br />
nei moduli che usano la funzione/variabile)<br />
devono co<strong>in</strong>cidere al 100%<br />
<strong>in</strong>t f( <strong>in</strong>t x ) ;<br />
Errore!<br />
extern <strong>in</strong>t f( double x );<br />
14<br />
<strong>in</strong>t f(<strong>in</strong>t x)<br />
{ . . . }<br />
libreria.c<br />
<strong>in</strong>t ma<strong>in</strong>(void)<br />
{ . . .<br />
y = f ( z ) ;<br />
. . .}<br />
client.c<br />
Ing. Paolo Bernardi
Il ruolo degli header file<br />
– Si possono utilizzare degli header file (file .h) per<br />
raccogliere le def<strong>in</strong>izioni extern di funzioni e<br />
variabili pubbliche<br />
– Il creatore del modulo che esporta le funzioni o<br />
variabili crea un file .h<br />
– Tutti i moduli che usano tali risorse esportate<br />
<strong>in</strong>cludono i file .h (#<strong>in</strong>clude).<br />
15<br />
Ing. Paolo Bernardi
Esempio di header file (I)<br />
void InsertionSort(<strong>in</strong>t A[], <strong>in</strong>t n) ;<br />
extern <strong>in</strong>t n_elem ;<br />
extern double vett[MAX] ; sort.h<br />
#<strong>in</strong>clude "sort.h" /* per controllo coerenza */<br />
<strong>in</strong>t n_elem ;<br />
double vett[MAX] ;<br />
sort.c<br />
static <strong>in</strong>t temp ;<br />
void InsertionSort(<strong>in</strong>t A[], <strong>in</strong>t n)<br />
{ . . . }<br />
static void CancellaVettore(<strong>in</strong>t A[], <strong>in</strong>t n)<br />
{ . . . }<br />
16<br />
Ing. Paolo Bernardi
Esempio di header file (II)<br />
void InsertionSort(<strong>in</strong>t A[], <strong>in</strong>t n) ;<br />
extern <strong>in</strong>t n_elem ;<br />
extern double vett[MAX] ; sort.h<br />
#<strong>in</strong>clude "sort.h" /* per importare dichiarazioni */<br />
<strong>in</strong>t ma<strong>in</strong>(void)<br />
{ <strong>in</strong>t v[10] ;<br />
/* … */<br />
client.c<br />
InsertionSort(v, 10) ;<br />
/* … */<br />
for(i=0; i
Note<br />
– Ciascun modulo solitamente esporta alcune<br />
funzioni o variabili, e ne importa altre<br />
– È sempre bene che ciascun modulo <strong>in</strong>cluda<br />
anche il proprio header file<br />
– Conviene limitare al m<strong>in</strong>imo il numero di variabili<br />
condivise<br />
– Includere anche le #def<strong>in</strong>e nei file .h<br />
– È buona norma raccogliere tra loro gruppi di<br />
moduli correlati per funzionalità<br />
• possono condividere l’header file.<br />
18<br />
Ing. Paolo Bernardi
Inclusione multipla di librerie<br />
• L’uso delle librerie può orig<strong>in</strong>are problemi <strong>in</strong><br />
fase di compilazione<br />
• Se 2 moduli differenti di uno stesso progetto<br />
<strong>in</strong>cludono la stessa libreria, il compilatore<br />
– rileverà variabili e funzioni def<strong>in</strong>ite 2 volte<br />
– <strong>in</strong>terromperà il processo di compilazione del<br />
progetto.<br />
19<br />
Ing. Paolo Bernardi
Inclusione multipla di librerie (cont.)<br />
20<br />
• Utilizzo di direttive per il compilatore<br />
• Alla prima <strong>in</strong>clusione la variabile di compilazione MNEMONICO<br />
non è ancora stata def<strong>in</strong>ita, qu<strong>in</strong>di il file viene l<strong>in</strong>kato<br />
regolarmente<br />
• Alla seconda, essendo MNEMONICO già stato def<strong>in</strong>ito, il<br />
contenuto del file non viene ricaricato.<br />
#ifndef MEMONICO<br />
#def<strong>in</strong>e MEMONICO<br />
void InsertionSort(<strong>in</strong>t A[], <strong>in</strong>t n) ;<br />
extern <strong>in</strong>t n_elem ;<br />
extern double vett[MAX] ;<br />
#endif<br />
Identificatore univoco all’<strong>in</strong>terno<br />
del progetto, ovvero 1<br />
MNEMONICO differente per file<br />
sort.h<br />
Ing. Paolo Bernardi
Filosofia della<br />
programmazione multi-file<br />
21<br />
• Il l<strong>in</strong>guaggio C mette a disposizione del<br />
programmatore pochi semplici strumenti che,<br />
accompagnati ad una opportuna e necessaria<br />
discipl<strong>in</strong>a di programmazione, consentono di gestire<br />
<strong>in</strong> maniera ottimale progetti software di grandi<br />
dimensioni [Luca Durante]<br />
• Il kernel di L<strong>in</strong>ux, circa 100 MB di sorgente,<br />
suddiviso <strong>in</strong> circa 8000 di file sorgenti, è scritto <strong>in</strong><br />
massima parte <strong>in</strong> C (e <strong>in</strong> assembler la restante<br />
parte) da cent<strong>in</strong>aia di programmatori sparsi per il<br />
mondo, che spesso non si sono neppure mai<br />
<strong>in</strong>contrati.<br />
Ing. Paolo Bernardi
Sviluppo di grandi progetti<br />
• Necessita di organizzazione e s<strong>in</strong>cronismo<br />
• Def<strong>in</strong>izione delle <strong>in</strong>terfacce, cioè del<br />
contenuto degli header file<br />
– Per ciascuna funzione sono specificate le<br />
funzionalità<br />
• Ripartizione delle risorse<br />
– 1 modulo di libreria = 1 programmatore.<br />
22<br />
Ing. Paolo Bernardi
Sviluppo di grandi progetti (cont.)<br />
23<br />
• Uso dei cosiddetti repository<br />
– 1 versione gold del progetto (obbligatoriamente<br />
funzionante) memorizzata su un server remoto<br />
– N versioni locali a disposizione dei s<strong>in</strong>goli programmatori<br />
per valutare le soluzioni sul modulo di propria competenza<br />
• Il programmatore impegnato nello sviluppo proprio<br />
modulo<br />
– Scarica <strong>in</strong> locale l’<strong>in</strong>tero progetto gold (update)<br />
– Lo modifica e valuta l’efficacia del suo <strong>in</strong>tervento<br />
– Se soddisfacente, lo carica sul server all’<strong>in</strong>terno del<br />
progetto gold (commit)<br />
• È mantenuta la “storia” dei commit.<br />
Ing. Paolo Bernardi
Sviluppo di grandi progetti (cont.)<br />
Implementazione<br />
LOCAL<br />
LOCAL<br />
Update<br />
Commit<br />
Update<br />
Commit<br />
24<br />
Def<strong>in</strong>izione delle<br />
specifiche<br />
GOLD<br />
Ing. Paolo Bernardi