Sei sulla pagina 1di 21

2

Struttura modulare
 Per semplificare la struttura di un programma
complesso è possibile suddividerlo in moduli
Un modulo è un blocco di codice che assolve
Le funzioni 
ad un compito preciso (ad es. calcola la radice
quadrata) e a cui è stato dato un nome
 Un programma consta di un modulo principale
Ver. 2.4 (il main) ed eventuali altri moduli di supporto
 Quando un modulo richiama un altro modulo,
il chiamante viene sospeso finché il chiamato
non ha terminato la sua esecuzione
 In C i moduli sono chiamati funzioni
 Ogni funzione può richiamare (far eseguire)
© 2010 - Claudio Fornaro - Corso di programmazione in C qualsiasi altra funzione (anche se stessa)

3 4

Struttura modulare Struttura modulare


 Le due chiamate del modulo StampaCiao  Vantaggi della programmazione modulare:
fanno eseguire ogni volta le istruzioni che lo  poiché i moduli “nascondono” al loro interno i
costituiscono (sospendendo il chiamante) dettagli di come una certa funzionalità venga
Modulo chiamante realizzata, il programma complessivo ha un livello
...
... di astrazione maggiore: il modulo viene visto come
scanf...
scanf... un insieme di macro-istruzioni (“black-box”)
StampaCiao()
StampaCiao()  il codice per ottenere una certa funzionalità viene
...
... Modulo chiamato (StampaCiao) scritto una volta sola e viene richiamato ogni volta
printf("###
printf("### ## ## ###\n");
for
for ...
... printf("#
printf("#
###\n");
## ## ## #\n");
#\n");
che è necessario (ma la chiamata richiede tempo)
switch
switch ...
...
printf...
printf("#
printf("#
printf("#
## ## ## ## #\n");
## ###
#\n");  il codice complessivo è più corto
printf... printf("# ### ## #\n");
#\n");
...
... printf("### # # # ###\n");
printf("### # # # ###\n");  essendo più piccoli, i moduli sono più semplici da
StampaCiao()
StampaCiao()
realizzare e da verificare
if
if (x==2)
(x==2) then
then  il codice di un modulo correttamente funzionante
...
... può essere riutilizzato in altri programmi
5 6

Variabili locali Parametri e valore restituito


 Ogni funzione è un piccolo programma a sé  Essendo le variabili interne locali, private, per
stante, isolato dalle altre funzioni passare ad una funzione i dati da elaborare è
 All’interno di una funzione possono essere necessario utilizzare variabili speciali dette
definite delle variabili locali (cioè hanno scope parametri
locale): le altre funzioni non le “vedono”  La funzione comunica al modulo chiamante il
 Variabili con lo stesso nome in funzioni diverse risultato della sua elaborazione producendo
sono quindi completamente scorrelate un unico valore detto valore restituito o valore
(possono dunque anche essere di tipo diverso) di ritorno
 Vengono create ogni volta che si entra nella parametri
funzione e distrutte (perdendo il valore)
quando si esce risultato
 Le inizializzazioni avvengono ad ogni chiamata, main
funzione
senza inizializzazione il contenuto è indefinito

7 8

Definizione di funzioni Definizione di funzioni


tipo nomeFunzione(parametri)  Se la funzione non ha parametri (ad esempio
{ StampaCiao), si indica void tra le
definizione_variabili_locali corpo parentesi:
istruzioni della void StampaCiao(void)
eventuale return funzione
} Se non si indica nulla, il compilatore C non
 tipo indica il tipo del valore restituito attua alcun controllo sui parametri (invece per
(es. sqrt restituisce un double) un compilatore C++ è come se l’argomento
 Se la funzione non restituisce valori (ad fosse esplicitamente void)
esempio StampaCiao visualizza soltanto)
bisogna indicare il tipo void:
void StampaCiao(.....)
Se non si mette nulla viene supposto int
9 10

Chiamata di funzione Ritorno da una funzione


 Si chiama una funzione indicandone il nome  La funzione termina (cioè l’esecuzione torna al
seguito da una coppia di parentesi contenenti modulo chiamante) quando viene eseguita
i valori da elaborare (separati da virgole) l’istruzione
eleva(y,2); return risultato;
 Se la funzione non richiede parametri, le  Una funzione può avere più istruzioni return
parentesi sono vuote, ma devono esserci:  risultato è il valore restituito dalla funzione al
StampaCiao(); chiamante, è un’espressione qualsiasi
return x*2;
 Il valore restituito può essere assegnato ad
una variabile o utilizzato in un’espressione,  Se il tipo restituito dalla funzione è void, non
altrimenti viene semplicemente scartato: si deve indicare risultato, inoltre la return
x = eleva(y,2); che precede la graffa di chiusura (solo
y = 3*eleva(2,k)-4*k; questa) può essere omessa
eleva(3,5);  I valori delle variabili locali vengono persi

11 12

Ritorno da una funzione Esempio di funzione


 Nel main la return termina il programma #include <stdio.h>
 Per terminare un programma dall’interno di int eleva(int b, int e)
una funzione (e passare lo status al Sistema {
Operativo) si utilizza la funzione int k=1;
exit(status) while (e-- > 0)
dichiarata in <stdlib.h>: k *= b;
exit(EXIT_SUCCESS); return k;
}
Continua (stesso file)...
13 14

Esempio di funzione Tipo di una funzione


Continuazione (stesso file)  Il tipo di una funzione è determinato dal
main() tipo del valore restituito e dal tipo, numero e
{ ordine dei suoi parametri
int x, y; int eleva(int b, int e)
printf("Introduci numero: "); eleva è una funzione che ha un primo
scanf("%d", &x); paramero int, un secondo parametro int e
y = eleva(x, 2);
restituisce un int
printf("%d^%d = %d\n", x,2,y);
return 0;
}
 Alla chiamata, la x del main viene copiata
nella b di eleva, mentre il 2 del main viene
copiato nella e di eleva

15 16

Scope di una funzione Scope di una funzione


 Lo scope di una funzione (nome e tipo) si  Il compilatore verifica che le chiamate a
estende dal punto in cui viene definita fino a funzione siano coerenti con le corrispondenti
fine file: la funzione può essere utilizzata solo definizioni (cioè abbiano lo stesso tipo)
dalle funzioni che nello stesso file seguono la  E’ necessario che la chiamata a funzione sia
sua definizione (vale anche per il main)
nello scope della funzione stessa
f1()
{...}  Se il compilatore trova una funzione di cui non
f2() conosce il tipo (non è in scope, ad esempio f1
{...} che chiama f2), allora presuppone che essa
main() sia definita altrove e quindi:
{...}  non fa controlli sugli argomenti
f1 non “vede” e non può quindi usare f2, f2  presuppone che restituisca un int
vede f1, il main vede f1 e f2  segnala il possibile problema con un Warning
17 18

Prototipo di una funzione Esempio di funzione


 Il prototipo di una funzione è una #include <stdio.h>
dichiarazione che estende lo scope della int eleva(int b, int e);  prototipo
funzione (nome e tipo)
main()
 Il corpo della funzione (la sua definizione)
può quindi essere collocato: {
 in un punto successivo a dove viene chiamata int x, y;
(nell’esempio seguente, eleva è definita dopo il printf("Introduci numero: ");
main dove viene utilizzata) scanf("%d", &x);
 in un altro file di codice sorgente C y = eleva(x, 2);
 in una libreria (compilata)
printf("%d^%d = %d\n", x,2,y);
 Lo scopo primario degli header file è quello di return 0;
fornire al compilatore i prototipi delle funzioni
delle librerie del C (ad es. stdio.h contiene i }
prototipi di scanf, printf, getchar, etc.) Continua (stesso file)...

19 20

Esempio di funzione Prototipo di una funzione


Continuazione (stesso file)  I prototipi possono essere collocati:
int eleva(int b, int e)  prima del main (come nell’esempio eleva)
{  tra una funzione e l’altra
int k=1;  insieme alle definizioni delle variabili locali di una
while (e-- > 0) funzione
k *= b;  Il prototipo estende lo scope della funzione
return k; (nome e tipo) dal punto dove è indicato:
}  fino a fine file se esso è collocato esternamente alle
funzioni (prima del main o tra due funzioni)
 fino a fine funzione se è interno ad una funzione
(collocato con le variabili locali di una funzione)
21 22

Prototipo di una funzione Parametri attuali e formali


 Il prototipo di una funzione è simile alla  Parametri attuali (o argomenti):
definizione della funzione, salvo che: sono i valori (variabili, costanti o espressioni)
 manca il corpo indicati tra le parentesi alla chiamata di una
i nomi dei parametri possono essere omessi (ma i

tipi devono essere presenti!)
funzione
 ha un punto e virgola alla fine
eleva(x,2)
int eleva(int, int);  Parametri formali:
 I nomi dei parametri dei prototipi: sono le variabili indicate tra le parentesi nella
 se non sono omessi, possono essere diversi da definizione della funzione
quelli usati nella definizione della funzione int eleva(int b, int e)
 sono scorrelati dagli altri identificatori (nomi uguali
si riferiscono comunque a identificatori diversi
 sono utili per descrivere il significato dei parametri:
int eleva(int base, int esponente);

23 24

Parametri attuali e formali Passaggio dei parametri


 Nella chiamata ad una funzione bisogna  I dati possono essere passati ad una funzione
indicare un argomento per ciascuno dei esclusivamente per valore (by value):
parametri formali alla chiamata della funzione vengono create
 I parametri attuali e quelli formali nuove variabili con i nomi di ciascuno dei
corrispondono in base alla posizione (il primo parametri formali e in esse viene copiato il
attuale al primo formale, etc.) valore del corrispondente parametro attuale
 I nomi dei parametri formali sono scorrelati
(e quindi tipicamente diversi) dai nomi di main()
eleva()
eventuali variabili usate come argomenti
x
(inoltre gli argomenti possono essere valori 2
b
costanti o il risultato di espressioni) e
 I parametri formali hanno lo stesso scope
delle variabili locali della funzione
25 26

Passaggio dei parametri Variabili locali static


 Come per le assegnazioni, se il parametro  Le variabili locali hanno classe di allocazione
attuale e il corrispondente formale sono di automatica: vengono create ogni volta che
tipo diverso c’è una conversione automatica al si esegue la funzione ed eliminate ogni volta
tipo del parametro formale (se è di tipo meno che questa termina (perdendone il valore)
capiente può essere generato un Warning)  Le variabili locali di classe di allocazione
 Poiché in memoria i parametri formali e quelli statica invece non vengono mai rimosse
attuali sono completamente distinti e dalla memoria per cui non perdono il loro
indipendenti, cambiare il valore di un valore quando la funzione termina (resta
parametro formale non modifica il parametro quello che aveva al termine della chiamata
attuale corrispondente, neppure se questo è precedente)
una semplice variabile (è ovviamente  Le variabili statiche non richiedono la
impossibile modificare una costante o il ri-allocazione della memoria ad ogni chiamata
risultato di un’espressione): nell’esempio visto della funzione, quindi il programma può
la modifica di b non si ripercuote su x essere più veloce

27 28

Variabili locali static Variabili locali static


 Si richiede una classe di allocazione statica e  L’inizializzazione delle variabili locali static
non automatica mediante lo specificatore di avviene idealmente solo la prima volta che si
classe di allocazione static: esegue la funzione (in realtà i valori vengono
static int cont = 0; inizializzati dal compilatore)
 Possono essere inizializzate dal compilatore
 Se non inizializzate esplicitamente, vengono
solo con espressioni costanti:
inizializzate automaticamente a 0 (che nel  numeri e #define
caso dei puntatori viene automaticamente  valori enum
convertito in NULL)  indirizzi di memoria di variabili statiche
 Non possono essere inizializzate con:
 valori const
 variabili e risultati di funzioni
 indirizzi di memoria di variabili automatiche
29 30

Variabili locali static Variabili locali static


 int conta(void)  char *nomeMese(int n)
{ {
static int cont = 0; static char *nome[] = {
return ++cont; "inesistente", "gennaio",
"febbraio", ecc... };
} if (n<1 || n>12)
Ogni volta che conta viene chiamata, essa return nome[0];
incrementa il contatore cont e ne restituisce else
il valore, se non fosse statica ma automatica return nome[n];
restituirebbe sempre il valore 1 perché cont }
verrebbe ri-inizializzata ogni volta a 0 La stringa di cui viene restituito il puntatore
può essere utilizzata dal chiamante in quanto
essendo statica non viene rimossa dalla
memoria

31 32

Passaggio dei parametri Passaggio dei parametri


 Il passaggio di parametri nella modalità #include <stdio.h>
per riferimento (by reference) prevede che void fz(int *);  prototipo
la modifica del parametro formale si main()
ripercuota identica sul corrispondente {
parametro attuale (deve essere una variabile) int x=2;
 In C non esiste il passaggio per riferimento, fz(&x);
ma questo può essere simulato passando per printf("%d\n", x);
return EXIT_SUCCESS;
valore alla funzione il puntatore al dato da
}
passare (che deve essere una variabile, non
può essere il risultato di un calcolo) void fz(int *p)
{
 Nella scanf le variabili scalari sono precedute *p = 12;
da & perché devono essere modificate dalla }
funzione e quindi se ne passa l’indirizzo
33 34

Passaggio dei parametri Passaggio dei parametri


 Nell’esempio: void swap(int *, int *);  prototipo
 il main alloca x, gli assegna il valore 2 e chiama main()
fz passandole l’indirizzo di x (&x) presente in una {
variabile temporanea (“senza nome”) int x=12, y=24;
 alla chiamata di fz, l’indirizzo di x (che è BF32F0) swap(&x, &y);
viene copiato by-value in p
}
 fz accede a x come *p, modificandola in 12
 quando fz termina, x vale 12 void swap(int *a, int *b)
{
main() int temp;
fz(int*p)
&x BF32F0 temp = *a;
x 2
BF32F0 p *a = *b;
fz(&x); *b = temp;
*p=12;
x  12 }

35 36

Passaggio di vettori Passaggio di vettori


 Per passare un vettore come argomento, si  Possono essere passati vettori con dimensioni
indica il suo nome senza parentesi diverse, ma dello stesso tipo T
x = media(vettore);
 Quando si passa un vettore-di-T, poiché si
 Il parametro formale corrispondente definirà
un vettore dello stesso tipo (in genere senza indica il nome del vettore, in realtà si passa
dimensione in quanto ininfluente) l’indirizzo di memoria del suo primo elemento
float media(int v[]); (e questa non dà alcuna informazione né
 La funzione deve conoscere in qualche modo restrizione sulla lunghezza del vettore)
la dimensione del vettore (indicarlo tra le  Il parametro formale è quindi in realtà un
parentesi quadre non serve a nulla): puntatore-a-T, la forma v[] viene convertita
 viene passato come argomento automaticamente in *v, si possono usare le
float media(int v[], int lung);
 è noto a priori (es. una #define) due definzioni indifferentemente
 si usano variabili esterne (vedere più avanti) float media(int *v);
37 38

Passaggio di matrici Passaggio di matrici


 Per passare una matrice come argomento, si  Possono essere passate matrici con diverso
indica il suo nome senza parentesi numero di righe, ma devono avere lo stesso
x = media(matrice); numero di colonne e gli elementi devono
 Il parametro formale corrispondente dichiara essere dello stesso tipo T
una matrice dello stesso tipo (in genere senza  Poiché una matrice è un vettore-di-vettori,
la prima dimensione in quanto ininfluente) quando essa viene passata ad una funzione,
void funz(int matrice[][10]) viene in realtà passato l’indirizzo del primo
 La funzione deve conoscere in qualche modo elemento del vettore-di-vettori
le dimensioni della matrice (indicarle tra le  Il tipo del parametro formale corrispondente è
parentesi quadre non serve a questo scopo) quindi un puntatore-a-vettore e NON un
puntatore-a-puntatore (il decadimento da
vettore a puntatore avviene una volta sola)

39 40

Passaggio di matrici Passaggio di matrici


 Nel caso dell’esempio, il parametro formale è  In memoria l’elemento Mx[i][j] viene
un puntatore-a-vettore-di-10-int: determinato con il calcolo
int (*matrice)[10]) indirizzo_di_Mx+NC*i+j
 La forma matrice[][10] viene convertita dove NC è il numero delle colonne,
automaticamente in (*matrice)[10], si come si vede il numero delle righe non serve
possono usare le due definzioni indifferentem.  Per passare ad una funzione una matrice con
void funz(int (*matrice)[10]) qualsiasi numero di righe e qualsiasi numero
 E’ quindi un errore scrivere: di colonne vi sono diverse soluzioni, si
void funz(int **matrice) rimanda alle slide relative all’allocazione
oltre all’errore di tipo, si perde la dimensione dinamica
delle colonne e quindi non si può determinare
la posizione degli elementi della matrice
41 42

Passaggio di vettori multidim. Parametri const


 Quanto visto per le matrici può essere esteso  Il modificatore const applicato ai parametri
ai vettori multidimensionali formali impedisce che all’interno della
 In particolare: funzione si possa modificarne il valore
 nel parametro formale si può tralasciare la
int funzione(const int v)
dimensione del solo primo parametro  Permette di proteggere i parametri da una
 il parametro formale è un puntatore ad un vettore successiva incauta modifica (per prevenire
di X vettori di Y vettori di Z vettori di .... di tipo T errori di programmazione)
 la funzione deve conoscere i valori di tutte le  Ad esempio, questo richiede al compilatore di
dimensioni segnalare se un puntatore-a-dato-costante
 possono essere passate matrici con la sola prima viene assegnato ad un puntatore-a-dato-
dimensione diversa variabile (ad esempio passandolo come
parametro), cosa che by-passerebbe la
restrizione

43 44

Parametri puntatori const Parametri puntatori const


1. Puntatore variabile a dati variabili 2. Puntatore variabile a dati costanti
int f(int *p) int f(const int *p) /* int const */
{ {*p = 12;  NO, dato costante
*p = 12;  OK, dato variabile p++;}  OK, puntatore variabile
p++; }  OK, puntatore variabile  Si può passare un int* (c’è conversione di tipo
 Si può passare un int* automatica in quanto si passa ad un tipo più
 Non si può passare un const int* restrittivo)
(non c’è conversione automatica di tipo perché  Si può passare un const int*
dentro la funzione nulla vieterebbe di poter  Note:
cambiare il valore alla variabile puntata):  Tipicamente utilizzato per passare un puntatore
int x=12; ad una struct o ad un vettore impedendo che
const int *y=&x; possano essere modificati
f(y);  ERRORE
 Non si può passare senza cast un Tipo** dove
è richiesto un const Tipo**
45 46

Parametri puntatori const Variabili esterne


3. Puntatore costante a dati variabili  Vengono definite (riservando memoria)
int f(int * const p) esternamente al corpo delle funzioni:
{*p = 12;  OK, dato variabile  in testa al file, tipicamente dopo le direttive
#include e #define
p++;}  NO, puntatore costante
 oppure tra una funzione e un’altra
 Si può passare un int*
 Sono visibili e condivisibili da tutte le funzioni
che nello stesso file seguono la definizione
4. Puntatore costante a dati costanti  Possono essere utilizzate come metodo
int f(const int * const p) alternativo per comunicare dati ad una
{*p = 12;  NO, dato costante funzione e per riceverne
p++;}  NO, puntatore costante  A questo scopo si usino con parsimonia:
 Si può passare un int* rendono poco evidente il flusso dei dati
all’interno del programma

47 48

Variabili esterne Variabili esterne


 Hanno classe di allocazione statica: non #include<...>
int uno;
vengono mai rimosse dalla memoria e, salvo main()
inizializzazione esplicita, vengono inizializzate {
automaticamente a 0 (i puntatori a NULL) uno = 12;
}
 Una variabile locale (interna ad una funzione) long due;
con lo stesso nome di una esterna copre la void fun1()
visibilità di quella esterna alla quale quindi { uno
uno = 21; due=55;
non può accedere con quel nome (non è }
buona pratica di programmazione) int tre; due
int fun2()
{ tre
return uno + due + tre;
}
49 50

extern extern
 Lo specificatore di classe di allocazione  La dichiarazione di una variabile extern può
extern permette di estendere la visibilità essere sia interna ad una funzione, sia esterna
delle variabili esterne #include<...>
 La clausola extern viene premessa ad una extern int due;  dichiarazione
definizione di variabile per trasformarla in funz4() esterna a funz
dichiarazione (non riserva memoria)
extern int x; {
extern long uno;  dichiarazione
 Indica al compilatore che la variabile è
definita (con allocazione di memoria, senza int tre; interna a funz
extern) altrove (più avanti nello stesso file o tre = uno + due;
in un altro file) }
 In seguito il linker ricondurrà tutte le long uno = 0, due = 0;  definizioni
dichiarazioni all’unica definizione di var esterne

51 52

extern Documentazione delle funzioni


 All’interno di una funzione, la clausola  E’ utile scrivere sempre la documentazione
extern associata ad una variabile la relativa allo scopo e all’uso di una funzione
identifica come variabile esterna definita dopo come commento iniziale contenente:
la funzione stessa o in altro file  Scopo: a cosa serve la funzione
 Nelle funzioni che, nello stesso file, seguono  Parametri: tipo e descrizione di ciascuno
la definizione di una variabile esterna, tutta la  Valore restituito: tipo e descrizione
dichiarazione (con extern) è opzionale Possibilmente anche:
fun3()  Pre-condizioni: requisiti particolari sui parametri che
{ extern long abc;  dichiarazione devono essere soddisfatti da chi invoca la funzione
abc = 12;}  utilizzo (es. param > 29)
long abc;  definizione  Post-condizioni: garanzie date dalla funzione sul
fun4() valore restituito o sullo stato del programma,
{ extern long abc;  dich. opzionale purché le precondizioni siano state soddisfatte
(es. risultato >= 23 && risultato <= 32)
abc = 21;}  utilizzo
53 54

Chiamata di funzione - dettagli Chiamata di funzione - dettagli


 Il programma compilato è costituito da due  Lo stack contiene inizialmente le variabili
parti distinte: locali della funzione main()
 code segment: codice eseguibile  Lo heap inizialmente è vuoto
STACK STACK
 data segment: costanti e variabili e serve per contenere i
note alla compilazione (statiche
ed esterne) blocchi di memoria
Stack e heap allocati dinamicamente con Stack e heap
 Quando il programma viene
funzioni malloc() (trattate
eseguito, il Sistema Operativo
in altre slide)
alloca spazio di memoria per: HEAP HEAP
 il code segment (CS)  Stack e heap crescono
DATA DATI
 il data segment (DS) nell’area condivisa nel senso
 lo stack e lo heap (condiviso)
CODE indicato dalle frecce CODICE

55 56

Chiamata di funzione - dettagli Chiamata di funzione - dettagli


 Quando viene chiamata una funzione, sullo  Nell’Activation Record viene anche
stack vengono prima copiati i valori dei suoi memorizzato l’indirizzo di ritorno dalla
argomenti e poi vi funzione: la locazione di memoria che contiene
viene allocato un l’istruzione del chiamante da cui continuare
STACK
Activation Record dopo che la funzione è terminata
(o stack frame) per  Queste operazioni di allocazione e
contenere tutte le Stack e heap deallocazione di spazio sullo stack e in
variabili locali (e altro) generale il meccanismo di chiamata e ritorno
 Quando la funzione da una funzione richiedono tempo
termina, gli argomenti e HEAP
 In casi estremi di necessità di elevate
l’AR vengono rimossi DATI performance si può cercare di limitare il
dallo stack che quindi numero delle chiamate a funzione, a costo di
CODICE
ritorna nello stato ricopiare lo stesso codice in più punti
precedente la chiamata (eventualm. utilizzando macro con argomenti )
57 58

Esercizi Esercizi
1. Si scriva un programma che per 10 volte 2. Si scriva un programma che chieda all’utente
chieda all’utente un valore e ne calcoli il 10 valori e di questi calcoli la radice quadrata.
logaritmo in base 2. Per il calcolo della radice quadrata si scriva
Per il calcolo del logaritmo si scriva una una funzione con prototipo:
funzione con prototipo: double radice(double a, double prec);
double log2(double a); che calcoli il valore approssimato della radice
che calcoli il valore utilizzando la formula: quadrata di a con il metodo di Newton:
1 a
log 2 x 
log e x xi 1   xi  
log e 2 2 xi 
xi sono approssimazioni successive della radice
quadrata di a. Si assuma x0= a e si iteri
fintanto che xi – xi+1 > prec.

59 60

Esercizi Esercizi
3. Si scriva una funzione con prototipo: 4. Si scriva una funzione con prototipo:
double media(double v[], int len); void rovescia(char s[]);
che calcoli e restituisca la media di un vettore che rovesci la stringa passata come
di double passato come argomento. argomento (modifica del parametro). Si scriva
Si scriva un programma che riempia due un programma che chieda una stringa, la
vettori di lunghezza differente (es. a[8] e passi a rovescia e la visualizzi.
b[10], li passi a media e visualizzi il risultato 5. Si scriva una funzione con prototipo:
per ciascuno di essi. La funzione non esegua int contastr(char a[], char x[]);
che conti quante volte la stringa x sia
operazioni di input/output.
contenuta in a. N.B. “bb” in “bbb”: 2 volte
6. Si scriva una funzione undup che modifichi
una stringa eliminandone i caratteri duplicati:
esempio: “ciao come va?”  “ciao mev?”
61 62

Esercizi Esercizi
7. Si scriva una funzione con prototipo: 8. Si scriva la funzione sommaVett che calcoli la
void ordina(int v[], int len, somma di due vettori. Si scriva un main che
int ord); chieda la dimensione dei vettori (max 100),
che ordini in senso crescente e decrescente il ne chieda i valori, li passi alla funzione e
vettore di int passato come argomento. visualizzi il vettore dei risultati. Non si alteri il
Il senso dell’ordinamento venga indicato dal contenuto dei due vettori da sommare. La
parametro ord (decrescente=0, crescente=1). funzione non faccia input/output.
Si scriva un main di test. Per non usare variabili esterne o variabili
locali static, alla funzione bisogna passare
anche il vettore dei risultati (il contenuto
iniziale non è rilevante).

63 64

Esercizi Esercizi
9. Un file di testo denominato Parole.txt 10. Si scriva una funzione con prototipo:
contiene una lista di parole, una per riga. double media(double M[][10],
Non è noto a priori di quante righe sia int righe, int colonne);
composto. Si scriva un programma che che calcoli e restituisca la media di una
chieda all’utente di introdurre una parola e generica matrice di 10 colonne passata come
visualizzi tutte le parole presenti nel file che argomento. Si scriva un programma che
anagrammate danno la parola introdotta. Si riempia due matrici di dimensioni richieste
scrivano due funzioni: una che riordina all’utente (massime 8x10 e 12x10, il massimo
alfabeticamente le lettere di una stringa di 10 colonne ciascuna è per coerenza con il
(“telefono”  “eeflnoot”) e un’altra che prototipo), le passi a media e visualizzi il
modifichi una stringa trasformandone tutti i risultato per ciascuna di esse.
caratteri in minuscolo. La funzione non faccia input/output.
65 66

Esercizi Esercizi
11. Scrivere una funzione con prototipo Soluzione a stati
int parseToIntVect(char *stringa, ws=white space
int vett[], int n); t=stringa per contenere i caratteri da trasf. in numero
cont=contatore valori
che analizzi la stringa data estraendo da
c=carattere i-esimo della stringa ct[i]
essa i primi n valori numerici interi e li
inserisca nel vettore vett[]. Se vi sono più cont++
di n valori, i successivi vengano ignorati. La !ws
!ws ct[0]

funzione restituisca il numero di elementi letti FUORI DENTRO


(che possono quindi essere meno di n). Si ws
ws
supponga che il file non contenga caratteri tvett[cont]

diversi da cifre e whitespace. Si scriva un


no-op tvett[cont]
main di test.

67 68

Esercizi Progetti multi-file


12. Siscriva un programma che permetta di  E’ possibile suddividere le funzioni che
calcolare il prodotto di 2 numeri interi senza costituiscono un eseguibile su più file (detti
segno introdotti dall’utente e composti translation unit )
ciascuno da un massimo di 1000 cifre.  Ciascun file viene compilato separatamente e
Conviene modularizzare il codice utilizzando il linker li assembla per costituire un unico file
opportune funzioni di supporto (ad esempio eseguibile
per moltiplicare un numero per una cifra, per  Uno solo dei file deve contenere la definizione
della funzione principale main
fare lo shift di n posizioni a sinistra di un
numero e per sommare due numeri).  L’insieme dei file sorgenti viene spesso
chiamato progetto
 In ciascun file si collocano funzioni che
insieme forniscono una certa funzionalità (ad
es. la gestione di uno stack)
69 70

Progetti multi-file Progetti multi-file


 Ciascuno dei file si comporta come una  Ciascun file ha bisogno delle sole direttive
libreria di funzioni, salvo che queste vengono #include e #define necessarie al codice di
compilate (e non solo linkate) con il quel file
programmma principale  Per usare in un file una funzione dichiarata in
 Un file con funzioni specifiche per fornire una un altro file (non può essere static), si deve
determinata funzionalità può essere indicarne il prototipo (extern opzionale)
facilmente riutilizzato in altri programmi:  Spesso si raggruppano tutte le #define, le
basta includerlo nel progetto variabili esterne e i prototipi di tutte le funzioni
(non può essere static) di un progetto in un
unico file .h e i file .c del progetto che ne
abbisognano lo includono (con virgolette):
#include "mioheader.h"

71 72

Progetti multi-file Progetti multi-file – extern


 Le variabili esterne usate in tutti i file devono  La clausola extern permette di usare una
essere definite solo in uno dei file, mentre gli variabile esterna anche in altri file, purché
altri devono avere la dichiarazione extern faccia parte dello stesso progetto
corrispondente (vedere le slide sulla  È compito del linker:
compilazione condizionale)  controllare che le varie dichiarazioni siano conformi
 Non si può usare extern con variabili alla definizione
esterne con specificatore di classe di  controllare che la definizione sia unica
allocazione static in un altro file (sono  associare definizione e dichiarazioni alla stessa
utilizzabili solo dalle funzioni di quel file) zona di memoria
73 74

Progetti multi-file – static Linkage di variabili e funzioni


 Le variabili esterne con specificatore di classe  Il linkage esprime la corrispondenza di
di memorizzazione static hanno scope identificatori (variabili o costanti) omonimi
limitato alle funzioni definite in quel file (solo presenti in più blocchi e/o in più translation
a quelle successive alla definizione): sono unit diverse (linkate insieme) e/o in librerie
“private” ad uso esclusivo delle funzioni di  Un identificatore con linkage esterno è
quel file visibile in più translation unit (es. variabili e
static int funzione(int x) funzioni esterne non static)
 La keyword static si riferisce alla funzione,  Un identificatore con linkage interno è
visibile solo nella translation unit dove è
non al valore restituito definito (es. variabili e funzioni static)
 Un identificatore non ha linkage se è locale
al blocco dove è definito (es. variabili locali,
parametri di funzioni, tag, membri, etc.)

75 76

Linkage di variabili e funzioni Lo stack


 Un identificatore con linkage esterno è  Uno stack (o pila) è una struttura dati di tipo
significativo almeno nei primi 6 caratteri, LIFO (Last In First Out) in cui i valori vengono
maiuscole e minuscole rappresentano
prelevati nell’ordine inverso a quello di
caratteri uguali
introduzione
 Un identificatore con linkage interno è
significativo almeno nei primi 31 caratteri,  Introduzione: push
maiuscole e minuscole rappresentano  Prelievo: pop
caratteri diversi
 Il linkage di un identificatore preceduto dalla
clausola extern è quello stabilito dalla sua
precedente dichiarazione nella stessa C
translation unit (ad es. una dichiarazione con B B B
extern relativa ad una variabile definita A A A A A
static più sopra nel file ha linkage interno)
77 78

Esercizi La coda
13. Scriverein un file separato la realizzazione di  Una coda è una struttura dati di tipo FIFO
uno stack basato su un vettore di interi. Si (First In First Out) in cui i valori vengono
realizzino le funzioni push e pop (con prelevati nello stesso ordine di introduzione
opportuni parametri ) che restituiscano 1 in
caso di errore (stack pieno o vuoto) e 0  Introduzione: enqueue
altrimenti (non devono fare I/O). Si scriva un  Prelievo: dequeue
main a menu in grado di verificarne il
funzionamento. I prototipi siano in un file .h.
A B A C B A
Il vettore deve essere static perché solo coda testa
push e pop ne abbiano accesso. Si utilizzi un
puntatore p che punti alla prima locazione
libera utilizzando le seguenti espressioni:
per push: *p++ = val; C B C
per pop: val = *--p;

79 80

Esercizi Il buffer circolare (coda)


14. Scrivere in un file separato la realizzazione di  Testa e coda non sono più fisse, ma si
una coda basata su un vettore di interi. Si rincorrono
scrivano le funzioni enqueue e dequeue (con
 E’ necessario un contatore delle posizioni
opportuni parametri ) che restituiscano 1 in
caso di errore (coda piena o vuota) e 0 libere per discriminare la condizione “pieno”
altrimenti (non devono fare I/O). Si scriva un da “vuoto” in cui testa e coda coincidono
main a menu in grado di verificarne il
funzionamento. I prototipi siano in un file .h. coda
4 coda testa
Si utilizzino due puntatori: testa punta alla cella C 3
con il prossimo valore da prelevare, coda punta 5 B
alla prossima cella da riempire. C B A
A
Il vettore sia static. 2
Si può notare che la enqueue è identica alla 0
push. 1 testa 5 4 3 2 1 0
81 82

Il buffer circolare (coda) Esercizio


 Aggiunta (in coda) 15. Scriverein un file separato la realizzazione di
if ( non pieno ) una coda come buffer circolare basato su un
aggiungi in coda vettore di interi. Si scrivano le funzioni
fa avanzare coda enqueue e dequeue con identico tipo di
posti liberi – 1 quelle dell’esercizio precedente così da
utilizzare lo stesso identico main.
Si noti che è necessario utilizzare un
 Prelievo (dalla testa) contatore delle posizioni libere (o occupate)
if ( non vuoto ) per verificare se è possibile inserire/togliere
preleva valore un valore, non c’è modo di stabilirlo
fa avanzare la testa confrontando semplicemente i puntatori testa
posti liberi +1
e coda.

Potrebbero piacerti anche

  • 21 LongJmp
    21 LongJmp
    Documento3 pagine
    21 LongJmp
    Riccardo Ricci
    Nessuna valutazione finora
  • 15 Struct
    15 Struct
    Documento10 pagine
    15 Struct
    openid_9xLqqfRO
    Nessuna valutazione finora
  • 07 Cicli
    07 Cicli
    Documento12 pagine
    07 Cicli
    openid_9xLqqfRO
    Nessuna valutazione finora
  • 02 LinguaggioC
    02 LinguaggioC
    Documento3 pagine
    02 LinguaggioC
    openid_9xLqqfRO
    Nessuna valutazione finora