Allocazione Statica Finora abbiamo visto che le variabili sono sempre definite staticamente. Questo è un limite perché la loro esistenza deve essere prevista e dichiarata a priori. In particolare per variabili di tipo array, in cui dover specificare a priori le dimensioni (costanti) è molto limitativo. Sarebbe molto utile poter allocare un array durante l esecuzione del programma (run-time) dopo aver scoperto quale deve essere la sua dimensione Allocazione Dinamica Per allocare nuova memoria durante l esecuzione del programma quando serve si usa una funzione di libreria che ne effettua la richiesta al sistema operativo: malloc() La funzione malloc(): chiede al sistema di allocare un area di memoria della dimensione specificata come parametro restituiscel indirizzo generico (void *)dell area di memoria allocata La funzione malloc() La funzione void *malloc(size_t dim): chiede al sistema di allocare un area di memoria grande dim byte restituisce l indirizzo dell area di memoria allocata (NULLse,per qualche motivo, l allocazione non è stata possibile) è sempre opportuno controllare il risultato di malloc()prima di usare la memoria fornita Il sistema operativo preleva la memoria richiesta da un area di memoria detta heap La funzione malloc() Uso: va specificato quanti byte si vogliono allocare attraverso il parametro passato a malloc() l indirizzo restituito dalla funzione va salvato in una variabile puntatore del tipo necessario Attenzione: malloc() restituisce un indirizzo puro, ossia un void * per assegnarlo a uno specifico puntatore occorre un cast esplicito
Esempio Allocazione dinamica di 16 byte per memorizzare numeri reali: float *p; p = (float*) malloc(16); Allocazione dinamica di 4 interi (qualunque sia la rappresentazione usata per gli interi): int *p; p = (int*) malloc(4*sizeof(int)); l operatore sizeofconsente di essere indipendenti dal sistema di elaborazione Tempo di vita Tempo di vita di una area dati dinamica non è legato a quello della funzione che la alloca quindi, una area dati dinamica può sopravvivere anche dopo che l esecuzione della funzione che l ha creata è terminata Ciò consente di creare un area dinamica in una funzione...... usarla in un altra funzione...... e distruggerla in una funzione ancora diversa Uso L area allocata è usabile, in maniera equivalente: tramite la notazione a puntatore ( *p) oppure tramite la notazione ad array( [ ] ) int *p; p=(int*)malloc(5*sizeof(int)); p[0] = 13; p[1] = 18;... *(p+4) = -20; Disallocazione Quando non serve più, l area allocata deve essere esplicitamente deallocata con una funzione che segnala al sistema operativo che quell area è da considerare nuovamente disponibile per altri usi. La deallocazionesi effettua mediante la funzione di libreria free() int *p=(int*)malloc(5*sizeof(int));... free(p);
Esempi Creazione un arraydi floatdi dimensione specificata dall utente #include <stdio.h> #include <stdlib.h> main() float *v; int n; printf("dimensione: "); scanf("%d",&n); v = (float*) malloc(n*sizeof(float));... uso dell array... free(v); Esempio Scrivere una funzione che, dato un intero, allochi e restituisca una stringa di caratteri della dimensione specificata #include <stdlib.h> char* alloca(int n) return (char*) malloc(n*sizeof(char)); NOTA: nella funzione non deve comparire la free(), in quanto scopo della funzione è proprio creare un arrayche sopravviva alla funzione stessa Array Dinamici Un arrayottenuto per allocazione dinamica è dinamico poiché le sue dimensioni possono essere decise durante l esecuzione del programma al momento della sua creazione e non a priori da chi scrive il programma. Non significa che l arraypossa essere espanso secondo necessità: una volta allocato, l array ha dimensione fissa Il modello di gestione della memoria dinamica del C richiede che l'utente si faccia esplicitamentecarico anche della deallocazione della memoria Nota: È un approccio pericoloso: molti errori sono causati proprio da un errata deallocazione. Si rischia di utilizzare puntatori che puntano ad aree di memoria non più esistenti (perché deallocate) dangling reference Esempio char *cp = NULL; /*... */ char c; cp = &c; /* c falls out of scope */ /* cp is now a dangling pointer */
Esempio di dangling pointer int * func ( void ) int num = 1234; /*... */ return # Il valore restituito dalla funzione è un dangling pointer. #include <stdlib.h> char *cp = malloc ( A_CONST ); /*... */ free ( cp ); /* cp now becomes a dangling pointer */ cp = NULL; /* cp is no longer dangling */ /*... */ int i; /* iintero */ int (*ip); /* ippuntatorea intero */ int *ip; /* come sopra*/ int (*ipa)[4]; /* ipa puntatore a un array di quattro interi.*/ int *iap[4]; /* attenzione: iapnon è puntatorea un array, ma è un array diquattropuntatoria int. */ Allocazione di un vettore void *vet_alloc(int n_elem, int dim) void *p; /* p punta al vettore di n_elem elementi */ p = malloc(n_elem * dim); if (p == NULL) printf("errore di memoria!\n"); exit(-1); return p; La chiamata int * vet = (int *)vet_alloc(10, sizeof(int));
float **mat_alloc(int rig, int col) float **p, *buf; int i; /* buf punta all'area dati di rig x col elementi */ buf = (float *)malloc(rig * col * sizeof(float)); /* p punta al vettore di puntatori alle righe */ p = (float **)malloc(rig * sizeof(float *)); if (!p) errore_all; exit(-1); /* inserisco in p i puntatori alle righe */ for(i=0; i<rig; i++, buf+=col) p[i] = buf; return p; La chiamata float ** mat = mat_alloc(n_row, n_col); Allocazione di matrice triangolare bassa int **mat_alloc(int rig) int **p; int i; /* p punta al vettore di puntatori alle righe */ p = (int **)malloc(rig * sizeof(int *)); if (!p) printf("errore di memoria!\n"); exit(-1); /* inserisco in p i puntatori alle righe */ for(i=0; i<rig; i++) p[i] = (int *)malloc((i+1) * sizeof(int)); if(!p[i])printf("memoria insufficiente!\n");exit(- 1); La chiamata per creare una matrice di nrighe int ** mat = mat_alloc(n); Inserimento di numeri casuali in matrice triangolare bassa #define MAX 255... void genera(int **mat, int n) int i,j; for(i=0; i<n; i++) for(j=0; j<=i; j++) mat[i][j] = rand()%(max +1); La chiamata per generare i valori compresi tra 0 e MAX da inserire nella matrice mat genera(mat, n); Stampa di matrice triangolare bassa void stampa_mat(int **mat, int n) int i,j; for(i=0; i<n; i++) for(j=0; j<=i; j++) printf("%d\t", mat[i][j]); printf("\n"); printf("\n\n"); La chiamata per stampare la matrice mat stampa_mat(mat, n);
Allocazione di un vettore di strutture di tipo struct dati struct dati *my_alloc(int n) struct dati *p; p = (struct dati *)malloc(n); if (!p) printf("errore di memoria!\n"); exit(-1); return p; La chiamata per allocare un vettore di struct dati di n elementi struct dati *info = my_alloc(n * sizeof(struct dati ));