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.