L`Allocazione Dinamica della Memoria

Transcript

L`Allocazione Dinamica della Memoria
L’Allocazione Dinamica della Memoria
Maurizio Palesi
DIIT—Università di Catania
Viale Andrea Doria 6, 95125 Catania
[email protected]
http://www.diit.unict.it/users/mpalesi
Sommario
Questo documento tratta l’allocazione dinamica della memoria in C. Verrà evidenziato come, alcune volte, l’utilizzo di strutture dati di tipo statico non sono opportune
in certi scenari applicativi. Saranno descritte in dettaglio le funzioni C per la gestione
dinamica della memoria e presentati diversi esempi.
Indice
1 Introduzione
2 Perchè Allocare Dinamicamente?
2.1 Sovradimensionamento . .
2.2 Allocazione Dinamica . .
3 Gestione Dinamica della Memoria
3.1 calloc() e malloc()
1
3.2 free() . . . . . . . . . .
3.3 realloc() . . . . . . .
2
4 Matrici Dinamiche
3
4 5 Esercizi
5
5
6
6
6
7
1 Introduzione
La scelta delle appropriate strutture dati è di fondamentale importanza per la risoluzione
di un certo problema almeno tanto quanto un buon programma di manipolazione. Fin
qui ci siamo occupati di strutture dati sia di tipo scalare (tipo intero, reale, carattere) sia
di tipo strutturato (stringhe, vettori, e strutture). A ciascuna di esse abbiamo associato le
relative rappresentazioni interne previste dal C. Le variabili corrispondenti ai tipi suddetti
non possono mutare le loro caratteristiche in fase di esecuzione e pertanto sono dette
statiche. Vogliamo occuparci ora di un altro tipo di variabili: quelle dinamiche cioè le
variabili che possono essere create e/o accresciute in fase di esecuzione.
1
2 Perchè Allocare Dinamicamente?
Il lettore potrebbe chiedersi qual è la necessità di utilizzare l’allocazione statica piuttosto
che quella dinamica. Si consideri per esempio il seguente frammento di codice per il
calcolo della media degli elementi di un vettore. Una prima versione potrebbe essere:
# define N 10
main ( )
{
i n t v [N ] , i , somma , media ;
/
/ inserimento dati
f o r ( i = 0 ; i <N ; i ++)
{
p r i n t f ( " I n s e r i s c i e l e m e n t o %d esim o : " ) ;
s c a n f ( "%d " , & v [ i ] ) ;
}
/ c a l c o l o d e l l a media e v i s u a l i z z a z i o n e
somma = 0 ;
f o r ( i = 0 ; i <N ; i ++)
somma = somma + v [ i ] ;
/
media = somma / N;
p r i n t f ( " La media è : % d " , media ) ;
}
Il problema in questo caso è che tale codice effettua la media esclusivamente di N valori. Se si volesse per esempio calcolare la media di 20 numeri piuttosto che di 10
occorrerebbe modificare N:
# define N 20
e ricompilare il programma. Una soluzione per per rendere il programma più flessibile
sarebbe quella di chiedere all’utente di inserire il numero di elementi di cui calcolare la
media e quindi utilizzare tale valore in luogo di N. Chiaramente una soluzione del tipo:
...
i n t num ;
p r i n t f ( " Di q u a n t i v a l o r i v u o i f a r e l a media ? " ) ;
s c a n f ( "%d " , & num ) ;
i n t v [ num ] ;
...
non è corretta in C visto che la dichiarazione di un vettore richiede la conoscenza del
numero di elementi a tempo di compilazione. Nel suddetto frammento di codice, infatti,
2
il valore della variabile num non è noto a tempo di compilazione ma soltanto a tempo di
esecuzione (in questo caso dopo l’esecuzione della scanf). Nelle sottosezioni successive presentiamo due diverse soluzioni a questo problema. La prima sovradimensiona il
vettore mentre la seconda fa uso dell’allocazione dinamica della memoria.
2.1
Sovradimensionamento
Una possibile soluzione potrebbe essere quella di sovradimensionare il vettore in questo
modo:
# d e f i n e MAXDIM 1 0 0
main ( )
{
i n t v [MAXDIM ] , num , i , somma , media ;
p r i n t f ( " Di q u a n t i v a l o r i v u o i f a r e l a media ? " ) ;
s c a n f ( "%d " , & num ) ;
c o n t r o l l o s e ho l e r i s o r s e n e c e s s a r i e /
/
/ per memorizzare t u t t i i d a t i /
i f ( num > MAXDIM)
p r i n t f ( " i l v e t t o r e òpu c o n t e n e r e a l massimo %d v a l o r i " ,
MAXDIM ) ;
else
{
/ inserimento dati /
f o r ( i = 0 ; i <num ; i ++)
{
p r i n t f ( " I n s e r i s c i e l e m e n t o %d esim o : " ) ;
s c a n f ( "%d " , & v [ i ] ) ;
}
/ c a l c o l o d e l l a media e v i s u a l i z z a z i o n e
somma = 0 ;
f o r ( i = 0 ; i <num ; i ++)
somma = somma + v [ i ] ;
media = somma / num ;
p r i n t f ( " La media è : % d " , media ) ;
}
}
3
/
Il problema di questo approccio è però lo spreco di memoria.
2.2
Allocazione Dinamica
Un approccio più efficiente dal punto di vista dell’utilizzazione della risorsa memoria sarebbe quello di utilizzare sempre un vettore formato da un numero di elementi
esattamente uguale a quelli da elaborare.
main ( )
{
i n t v , num , i , somma , media ;
p r i n t f ( " Di q u a n t i v a l o r i v u o i f a r e l a media ? " ) ;
s c a n f ( "%d " , & num ) ;
/ a l l o c o una à q u a n t i t d i memoria n e c e s s a r i a
/ p e r m e m o r i z z a r e e s a t t a m e n t e ’ num ’ i n t e r i
i l puntatore v àpunter esattamente
/
/
a l l ’ i n i z i o d i q u e s t a area
sizeof ( int ) ) ;
v = ( i n t ) m a l l o c ( num
/ inserimento dati
/
f o r ( i = 0 ; i <num ; i ++)
{
p r i n t f ( " I n s e r i s c i e l e m e n t o %d esim o : " ) ;
s c a n f ( "%d " , & v [ i ] ) ;
}
/ c a l c o l o d e l l a media e v i s u a l i z z a z i o n e
somma = 0 ;
f o r ( i = 0 ; i <um ; i ++)
somma = somma + v [ i ] ;
/
media = somma / num ;
p r i n t f ( " La media è : % d " , media ) ;
/ ora p o s s o l i b e r a r e l ’ a r e a d i memoria
/ precedentemente allocata
free (v );
}
4
/
/
/
/
/
/
Le funzioni malloc(), free() e diverse altre saranno discusse nella sezione successiva.
3 Gestione Dinamica della Memoria
In questa sezione descriveremo in dettaglio le funzioni messe a disposizione dalla libreria
standard C per la gestione dinamica della memoria. Le principali sono quattro: malloc(), calloc(), free(), ralloc() e sono accessibili includendo il file header
stdlib.h:
#include <stdlib.h>
malloc() e calloc() allocano un blocco di memoria, free() libera un blocco di
memoria precedentemente allocato e realloc() modifica le dimensioni di un blocco
di memoria precedentemente allocato.
3.1
calloc() e malloc()
Il prototipo delle funzioni malloc e calloc è il seguente:
c a l l o c ( i n t num_elementi , i n t dim_elemento ) ;
void
void malloc ( d i m _ t o t a l e ) ;
calloc() alloca memoria per un vettore di num_elementi elementi di dim_elemento
bytes ciascuno e restituisce un puntatore alla memoria allocata. Inoltre inizializza ogni
byte di tale blocco di memoria a zero. malloc() alloca dim_totale bytes di memoria e restituisce un puntatore a tale blocco. In questo caso la memoria non è inizializzata
a nessun valore.
Per determinare la dimensione in byte di un tipo di dato è possibile utilizzare la
funzione sizeof(). Quindi, per esempio, per allocare un vettore di n elementi di tipo
intero basta fare:
int v ;
v = ( int ) calloc (n , sizeof ( int ) ) ;
Il puntatore v punterà al primo elemento di un vettore di n elementi interi. Si noti che
è stato utilizzato il casting per assegnare il puntatore restituito da calloc a v. Infatti
calloc restituisce un void* mentre v è un int*. Analogamente si può utilizzare la
funzione malloc per ottenere lo stesso risultato:
int v ;
v = ( i n t ) malloc ( n
sizeof ( int ) ) ;
In questo caso occorre determinare il numero totale di bytes da allocare che è pari al
numero di elementi del vettore per la dimensione in bytes del generico elemento. In
questo caso poichè il vettore contiene interi, la dimensione del generico elemento è
sizeof(int).
5
Sia calloc() che malloc() restituiscono NULL se non riescono ad allocare la
quantità di memoria richiesta.
3.2
free()
Il prototipo della funzione free() è:
ptr );
void f r e e ( void
Essa rilascia (libera o dealloca) lo spazio di memoria puntato da ptr il cui valore proveniva da una precedente malloc() o calloc() o realloc(). Se ptr è NULL
nessuna operazione viene eseguita.
3.3
realloc()
Il prototipo della funzione realloc() è:
void
r e a l l o c ( void
ptr , int dim_totale ) ;
Essa modifica la dimensione di un blocco di memoria puntato da ptr a dim_totale
bytes. Se ptr è NULL, l’invocazione è equivalente ad una malloc(dim_totale).
Se dim_totale è 0, l’invocazione è equivalente ad una free(ptr). Naturalmente il valore di ptr deve provenire da una precedente invocazione di malloc() o
calloc() o realloc().
4 Matrici Dinamiche
Possiamo rappresentare una matrice come un vettore di vettori. Cioè l’elemento -esimo
di un vettore è un vettore che rappresenta le colonne della matrice. Per costruire una
matrice dinamica di r righe e c colonne basterà eseguire le seguenti due operazioni:
1. Allocare dinamicamente un vettore di r puntatori (vettore delle righe).
2. Per ogni riga allocare un vettore di c elementi del tipo base considerato (vettore
colonne).
La Figura 1 mostra la rappresentazione grafica di una matrice dinamica.
Per esempio il seguente frammento di codice definisce ed alloca una matrice dinamica di interi di r righe e c colonne.
{
int matrice , i ;
/ a l l o c o i l v e t t o r e d e l l e r i g h e . Ogni e l e m e n t o
/ d i q u e s t o v e t t o r e è un p u n t a t o r e
m at r i ce = ( i n t ) malloc ( r
sizeof ( int
));
6
/
/
0
1 2 3 4 5 6 7
8
0
1
2
3
4
Figura 1: Rappresentazione di una matrice dinamica vista come un vettore di puntatori.
/ per ogni riga al l oco l e colonne /
f o r ( i = 0 ; i < r ; i ++)
sizeof ( int ) ) ;
m at r i ce [ i ] = ( i n t ) malloc ( c
}
Per disallocare una matrice dinamica, invece, occorre procedere alla rovescia: prima si
disallocano tutte le colonne quindi si disalloca il vettore dei puntatori alle colonne.
{
/ per ogni riga d i s a l l o c o l e colonne /
f o r ( i = 0 ; i < r ; i ++)
free ( matrice [ i ] ) ;
disa lloco i l verrore de lle righe
/
free ( matrice ) ;
/
}
5 Esercizi
Esercizio 5.1 ()
Calcolare la media degli elementi di una sequenza di valori double inseriti
dall’utente. La fine della sequenza è identificata dal numero 0.0. Utilizzare
vettori dinamici.
7
Risoluzione
# i n c l u d e < s t d i o . h>
# i n c l u d e < s t d l i b . h>
v o i d main ( )
{
d ou b le v e t t o r e , e l e m e n t o , somma , media ;
int
i , j , fine ;
v e t t o r e = NULL ;
/ inserimento dati
/
i = 0;
fine = 0;
while ( ! f i n e )
{
p r i n t f ( " I n s e r i s c i un e l e m e n t o ( 0 p e r f i n i r e ) : " ) ;
s c a n f ( "% l f " , & e l e m e n t o ) ;
i f ( elemento = = 0 . 0 )
fine = 1;
else
{
/
/ aum ento d i 1 l a d i m e n s i o n e d e l v e t t o r e
s i z e o f ( d ou b le ) ) ;
v e t t o r e = ( d ou b le ) r e a l l o c ( v e t t o r e , ( i + 1 )
v e t t o r e [ i ] = elemento ;
i ++;
}
}
/ c a l c o l o e stam pa d e l l a media
somma = 0 . 0 ;
f o r ( j = 0 ; j < i ; j ++)
somma + = v e t t o r e [ j ] ;
/
media = somma / i ;
p r i n t f ( " La media è : % l f \ n " , media ) ;
/ dellocazione del ve tt or e
free ( vettore );
/
8
}
Esercizio 5.2 ()
Calcolare la trasposta di una matrice di interi allocata dinamicamente.
Risoluzione
# i n c l u d e < s t d i o . h>
# i n c l u d e < s t d l i b . h>
v o i d main ( )
{
int matrice , trasposta ;
int righe , colonne , r , c ;
p r i n t f ( " Q uante r i g h e ha l a m a t r i c e : " ) ;
s c a n f ( "%d " , & r i g h e ) ;
p r i n t f ( " Q uante c o l o n n e ha l a m a t r i c e : " ) ;
s c a n f ( "%d " , & c o l o n n e ) ;
/ allocazione della matrice /
m at r i ce = ( i n t ) malloc ( r i ghe
sizeof ( int
));
f o r ( r = 0 ; r < r i g h e ; r ++)
m at r i ce [ r ] = ( i n t ) malloc ( colonne
sizeof ( int ) ) ;
/ inserimento dati
/
f o r ( r = 0 ; r < r i g h e ; r ++)
f o r ( c = 0 ; c< c o l o n n e ; c ++)
{
p r i n t f ( " I n s e r i s c i e l e m e n t o d i r i g a %d e c o l o n n a %d : " , r , c ) ;
s c a n f ( "%d " , & m a t r i c e [ r ] [ c ] ) ;
}
/ alloco la matrice trasposta
/
) malloc ( colonne
sizeof ( int
));
trasposta = ( int
f o r ( c = 0 ; c< c o l o n n e ; c ++)
t r a s p o s t a [ c ] = ( i n t ) malloc ( r i ghe
sizeof ( int ) ) ;
9
/ calcolo della trasposta
/
f o r ( r = 0 ; r < r i g h e ; r ++)
f o r ( c = 0 ; c< c o l o n n e ; c ++)
trasposta [ c ][ r ] = matrice [ r ][ c ];
/ stam po l a t r a s p o s t a /
p r i n t f ( " La t r a s p o s t a è : \ n " ) ;
f o r ( c = 0 ; c< c o l o n n e ; c ++)
{
f o r ( r = 0 ; r < r i g h e ; r ++)
p r i n t f ( "%d " , t r a s p o s t a [ c ] [ r ] ) ;
pr i n t f ( "\ n" ) ;
}
/ dealloco la matrice /
f o r ( r = 0 ; r < r i g h e ; r ++)
free ( matrice [ r ] ) ;
free ( matrice ) ;
/
/ dealloco la trasposta
f o r ( c = 0 ; c< c o l o n n e ; c ++)
free ( trasposta [ c ] );
free ( trasposta );
}
Esercizio 5.3 ()
Calcolare la trasposta di una matrice di interi allocata dinamicamente. Strutturare il programma a funzioni.
Risoluzione
# i n c l u d e < s t d i o . h>
# i n c l u d e < s t d l i b . h>
typedef int
TMatrice ;
/ dichiarazione delle funzioni ( prototipi )
10
/
r , int c );
void LeggiRigheColonne ( i n t
TMatrice AllocaMatrice ( in t r , in t c ) ;
v o i d I n s e r i m e n t o ( T M a t r i c e mat , i n t r , i n t c ) ;
v o i d C a l c o l a T r a s p o s t a ( T M a t r i c e mat , T M a t r i c e t r a s p , i n t r , i n t c ) ;
v o i d V i s u a l i z z a M a t r i c e ( T M a t r i c e mat , i n t r , i n t c ) ;
v o i d D e a l l o c a M a t r i c e ( T M a t r i c e mat , i n t r ) ;
/ programma p r i n c i p a l e
/
v o i d main ( )
{
TMatrice m atrice , t r a s p o s t a ;
int righe , colonne , r , c ;
L e g g i R i g h e C o l o n n e (& r i g h e , & c o l o n n e ) ;
matrice = AllocaMatrice ( righe , colonne ) ;
Inserimento ( matrice , righe , colonne ) ;
p r i n t f ( " Hai i n s e r i t o l a m a t r i c e : \ n " ) ;
VisualizzaMatrice ( matrice , righe , colonne ) ;
t r a s p o s t a = AllocaMatrice ( colonne , righe ) ;
CalcolaTrasposta ( matrice , t r a s p o s t a , righe , colonne ) ;
p r i n t f ( " La t r a s p o s t a è : \ n " ) ;
VisualizzaMatrice ( t r a s p o s t a , colonne , righe ) ;
D e a l l o c a M a t r i c e (& m a t r i c e , r i g h e ) ;
D e a l l o c a M a t r i c e (& t r a s p o s t a , c o l o n n e ) ;
}
/ definizione delle funzioni
/
11
void LeggiRigheColonne ( i n t
r , int c )
{
p r i n t f ( " Q uante r i g h e ha l a m a t r i c e : " ) ;
s c a n f ( "%d " , r ) ;
p r i n t f ( " Q uante c o l o n n e ha l a m a t r i c e : " ) ;
s c a n f ( "%d " , c ) ;
}
TMatrice AllocaMatrice ( in t r , in t c )
{
T M a t r i c e mat ;
int i ;
sizeof ( int
));
mat = ( T M a t r i c e ) m a l l o c ( r
f o r ( i = 0 ; i < r ; i ++)
sizeof ( int ) ) ;
mat [ i ] = ( i n t ) m a l l o c ( c
r e t u r n mat ;
}
v o i d I n s e r i m e n t o ( T M a t r i c e mat , i n t r , i n t c )
{
int i , j ;
f o r ( i = 0 ; i < r ; i ++)
f o r ( j = 0 ; j <c ; j ++)
{
p r i n t f ( " I n s e r i s c i e l e m e n t o d i r i g a %d e c o l o n n a %d : " , i , j ) ;
s c a n f ( "%d " , & mat [ i ] [ j ] ) ;
}
}
v o i d C a l c o l a T r a s p o s t a ( T M a t r i c e mat , T M a t r i c e t r a s p , i n t r , i n t c )
{
int i , j ;
f o r ( i = 0 ; i < r ; i ++)
f o r ( j = 0 ; j <c ; j ++)
t r a s p [ j ] [ i ] = mat [ i ] [ j ] ;
}
12
v o i d V i s u a l i z z a M a t r i c e ( T M a t r i c e mat , i n t r , i n t c )
{
int i , j ;
f o r ( i = 0 ; i < r ; i ++)
{
f o r ( j = 0 ; j <c ; j ++)
p r i n t f ( "%d " , mat [ i ] [ j ] ) ;
pr i n t f ( "\ n" ) ;
}
}
void D e a l l o c a M a t r i c e ( TMatrice
{
int i ;
mat , i n t r )
f o r ( i = 0 ; i < r ; i ++)
f r e e ( ( mat ) [ i ] ) ;
f r e e ( mat ) ;
}
13