Strutture
Free
Strutture
Consideriamo l’esercizio assegnato la scorsa lezione per rappresentare il
libretto di uno studente.
Per memorizzare i dati si sono utilizzati tre array:
char* nomiEsami[MAX ESAMI]
Array dei nomi degli esami (MAX ESAMI è il massimo numero degli
esami).
int cfu[MAX ESAMI]
Array con i crediti degli esami.
int voti[MAX ESAMI]
Array con i voti degli esami.
Vogliamo ora migliorare la rappresentazione usando un unico array esami.
Strutture
Free
La struttura esame
Un esame può essere rappresentato da una terna
( nome, cfu, voto )
dove nome è il nome dell’esame (una stringa), cfu sono i crediti (un
intero) e voto è il voto (un intero).
Per rappresentare un esame usiamo la struttura struct esame, avente i
tre campi nome, cfu e voto.
struct esame{
char *nome;
int cfu;
int voto;
};
// nome
// crediti
// voto
Il campo nome è un puntatore all’array di char contenente il nome
dell’esame.
Strutture
Free
L’array esami
Per memorizzare gli esami usiamo il seguente array esami:
struct esame* esami[MAX_ESAMI];
Per ogni k ≥ 0, esami[k] punta a una struttura che rappresenta il
k-esimo esame inserito.
La struttura associata a un esame va creata dinamicamente usando
calloc.
Strutture
Free
L’array esami
Esempio
Supponiamo che gli esami inseriti siano
("mat 1", 12, 28)
("prog 1 ", 12, 26)
("fisica", 6, 27)
Allora:
esami[0] punta a una struttura che rappresenta l’esame il cui nome
è "mat 1", i crediti sono 12 e il voto 28;
esami[1] punta a una struttura che rappresenta l’esame il cui nome
è "prog 1" , i crediti sono 12 e il voto 26;
esami[2] punta a una struttura che rappresenta l’esame il cui nome
è "fisica", i crediti sono 6 e il voto 27.
Strutture
Free
L’array esami
’m’ ’a’
’t’
’’
’1’ ’\0’
’p’
’r’
’o’
’g’
’’
’1’ ’\0’
’f’
’i’
’s’
’i’
’c’
’a’ ’\0’
12
28
esami[0]
esami[1]
esami[2]
12
26
.........
6
27
Strutture
Free
La funzione newEsame
La funzione
struct esame* newEsame(char* nome, int cfu, int voto)
crea una nuova struttura che rappresenta un esame il cui nome, crediti e
voto sono specificati dai parametri e restituisce l’indirizza della nuova
struttura.
Le operazioni da compiere sono:
Creazione di una nuova struttura di tipo struct esame.
Occorre definire una variabile newEs per memorizzare l’indirizzo della
nuova struttura (notare che newEs deve avere tipo struct esame*).
struct esame* newEs;
newEs = malloc(sizeof(struct esame));
Ora newEs punta alla struttura creata da malloc.
Strutture
Free
La funzione newEsame
Inizializzazione dei campi della nuova struttura.
Per i crediti e i voti occorre eseguire gli assegnamenti
newEs->cfu = cfu;
newEs->voto = voto;
L’espressione
newEs->cfu
equivale a
(*newEs).cfu
e denota il campo cfu della struttura a cui newEs si riferisce.
La prima istruzione assegna al campo cfu della nuova struttura il
valore di cfu (secondo parametro della funzione).
La seconda istruzione assegna al campo voto della nuova struttura il
valore di voto (terzo parametro della funzione).
Strutture
Free
La funzione newEsame
Per inizializzare il campo nome della nuova struttura, occorre creare
dinamicamente un nuovo array, da assegnare a newEs->nome, e
copiare in esso il nome dell’esame (stringa a cui punta il parametro
nome della funzione).
newEs->nome = calloc(strlen(nome)+1, sizeof(char));
strcpy(newEs->nome, nome);
Infine, la funzione deve restituire l’indirizzo della nuova struttura
(valore della variabile newEs).
return newEs;
Strutture
Free
La funzione newEsame
struct esame* newEsame(char* nome, int cfu, int voto){
struct esame* newEs;
newEs = malloc(sizeof(struct esame));
newEs->nome = calloc(strlen(nome)+1, sizeof(char));
strcpy(newEs->nome, nome);
newEs->cfu = cfu;
newEs->voto = voto;
return newEs;
}
Strutture
Free
Lettura e inserimento di un esame
Per leggere un esame occorre compiere le seguenti operazioni:
Vanno letti da standard input nome, crediti e voto dell’esame.
Per leggere il nome dell’esame (che può contenere spazi) va usata
una apposita funzione, ad esempio la funzione readEsame definita la
scorsa lezione.
Quando i dati relativi a un esame sono stati letti, va creata una
nuova struttura esame usando la funzione newEsame in cui vanno
inseriti i dati letti.
Occorre assegnare poi a esame[k] l’indirizzo della nuova struttura
(k vale inizialmente 0 e va incrementato dopo ogni inserimento).
Strutture
Free
La funzione main
int main(void){
struct esame* esami[MAX_ESAMI];
char nome[MAX_LEN + 1]; // array usato per leggere il nome di un esame
int c,k,cfu,voto;
c = getchar(); // leggi il primo carattere
k = 0;
while( c != ’q’){ // termina quando viene letto ’q’
switch(c){
case ’+’: // inserisci un esame
readEsame(nome); // legge il nome dell’esame
scanf("%d%d", &cfu, &voto );
esami[k] = newEsame(nome,cfu,voto);
k++;
break;
case ’p’: ... // stampa libretto
}// end switch
c = getchar(); // leggi prossimo carattere
}// end while
return 0;
}// end main
Strutture
Free
Esempio di esecuzione
Supponiamo che l’esame da inserire sia ("mat 1", 12, 28).
Nella funzione main, dopo la lettura dei dati di input viene eseguita la
chiamata
newEsame(nome,cfu,voto)
Il primo argomento è l’indirizzo dell’array nome definito in main, che
contiene il nome dell’esame;
Il secondo argomento è 12 (valore variabile cfu di main);
Il terzo argomento è 28 (valore variabile voto di main).
Strutture
Free
Esempio di esecuzione
newEsame
main
nome
cfu
voto
newEs
12
28
nome[0]
nome[1]
nome[2]
nome[3]
nome[4]
nome[5]
’m’
’a’
’t’
’’
’1’
’\0’
.........
esami[0]
esami[1]
.........
Strutture
Free
Esempio di esecuzione
Viene creata una nuova struttura esame e assegnata a newEs
newEsame
nome
main
cfu
voto
newEs
12
28
nome[0]
nome[1]
nome[2]
nome[3]
nome[4]
nome[5]
’m’
’a’
’t’
’’
’1’
’\0’
.........
esami[0]
esami[1]
.........
Strutture
Free
Esempio di esecuzione
L’istruzione
newEs->nome = calloc(strlen(nome)+1,sizeof(char));
crea un nuovo array di 6 elementi ( strlen("mat 1") vale 5) e il
suo indirizzo viene assegnato a newEs->nome (ossia, al campo nome
della struttura a cui la variabile newEs punta).
L’ istruzione
strcpy(newEs->nome, nome);
copia nel nuovo array la stringa "mat 1".
Infine, le istruzioni
newEs->cfu = cfu;
newEs->voto = voto;
assegnano ai campi cfu e voto dell nuova struttura i valori delle
variabili (parametri) cfu e voto definite in newEsame.
Strutture
Free
Esempio di esecuzione
newEsame
main
nome
cfu
voto
newEs
12
28
nome[0]
nome[1]
nome[2]
nome[3]
nome[4]
nome[5]
’m’
’a’
’t’
’’
’1’
’\0’
.........
esami[0]
esami[1]
.........
’m’ ’a’
12
28
’t’
’’
’1’ ’\0’
Strutture
Free
Esempio di esecuzione
La funzione newEsame termina l’esecuzione con l’istruzione
return newEs;
che restituisce il valore della variabile newEs, ossia l’indirizzo della
nuova struttura.
Il controllo ritorna alla funzione main che completa l’esecuzione
dell’istruzione
esami[k] = newEsame(nome,cfu,voto); // k vale 0
assegnando a esami[0] l’indirizzo restituito dalla chiamata a
newEsame.
Strutture
Free
Esempio di esecuzione
newEsame
main
nome
cfu
voto
newEs
12
28
nome[0]
nome[1]
nome[2]
nome[3]
nome[4]
nome[5]
’m’
’a’
’t’
’’
’1’
’\0’
.........
esami[0]
esami[1]
.........
’m’ ’a’
12
28
’t’
’’
’1’ ’\0’
Strutture
Free
Esempio di esecuzione
Quando il record di attivazione di newEsame è tolto dalla memoria,
l’array esami è:
’m’ ’a’
’t’
’’
’1’ ’\0’
12
28
esami[0]
esami[1]
esami[2]
.........
Esercizio
Scrivere il codice della funzione
printLibretto(esami, int n)
che, dato l’array degli esami come primo parametro e il numero n di
esami sostenuti come secondo parametro, stampa il libretto.
Strutture
Free
Deallocazione della memoria
Supponiamo di voler cancellare l’esame appena inserito.
Occorre rilasciare la memoria precedentemente allocata tramite le
funzioni malloc e calloc in modo che possa essere riutilizzata per
successive allocazioni.
La chiamata
free(p)
rilascia la memoria a cui punta p.
Si assume che il valore di p sia l’indirizzo di un blocco di memoria
precedentemente allocato tramite malloc o calloc.
Strutture
Free
Deallocazione della memoria
Esempio
La chiamata
free(esami[0])
rilascia lo spazio di memoria occupato dalla struttura a cui esami[0] si
riferisce
’m’ ’a’
esami[0]
esami[1]
esami[2]
.........
’t’
’’
’1’ ’\0’
Strutture
Free
Deallocazione della memoria
Come si nota dalla figura, ci sono due problemi:
(1) esami[0] contiene un indirizzo di memoria non più in uso (dangling
reference).
(2) È rimasto allocato in memoria il vettore usato per rappresentare la
stringa "mat 1" a cui ora non è più possibile accedere.
Per risolvere (1) si può porre
esami[0] = NULL;
// NULL e’ l’indirizzo 0
che segnala che esami[0] non contiene alcun indirizzo valido
Il punto (2) non è invece risolubile in quanto non è più noto l’indirizzo
del blocco di memoria da deallocare.
Strutture
Free
Deallocazione della memoria
Il modo corretto di rilasciare tutta la memoria usata per rappresentare
esami[0] è quello di rilasciare prima la memoria occupata dall’array
contenente la stringa "mat 1" e successivamente la memoria occupata
dalla struttura.
free(esami[0]->nome);
free(esami[0]);
esami[0] = NULL;
Esercizio
Aggiungere al programma un’operazione che permetta di eliminare
l’esame in posizione k.
Occorre poi modificare la funzione printLibretto facendo in modo che
non vengano stampati gli elementi dell’array esami (primo argomento
della funzione) per cui esami[k] è NULL.
Scarica

Strutture