Allocazione dinamica della memoria

 


 

Memoria stack e memoria heap

 

Abbiamo già sentito parlare dell'area di memoria stack: é quella in cui viene allocato un pacchetto di dati non appena l'esecuzione passa dal programma chiamante a una funzione. Abbiamo detto che questo pacchetto (il quale contiene l'indirizzo di rientro nel programma chiamante, la lista degli argomenti passati alla funzione e tutte le variabili automatiche definite nella funzione) viene "impilato" sopra il pacchetto precedente (quello del programma chiamante) e poi automaticamente rimosso dalla memoria appena l'esecuzione della funzione é terminata.
Sappiamo anche che, grazie a questo meccanismo, le funzioni possono essere chiamate ricorsivamente e inoltre si possono gestire funzioni con numero variabile di argomenti. Le variabili automatiche definite nella funzione hanno lifetime limitato all'esecuzione della funzione stessa proprio perché, quando la funzione termina, il corrispondente pacchetto allocato nell'area stack viene rimosso.

Un'altra area di memoria è quella in cui vengono allocate le variabili non locali e le variabili locali statiche. A differenza dalla precedente, quest'area viene mantenuta in vita fino alla fine del programma, anche se ogni variabile è visibile solo all'interno del proprio ambito.

Esiste una terza area di memoria che il programma può utilizzare. Questa area, detta heap, è soggetta a regole di visibilità e tempo di vita diverse da quelle che governano le due aree precedenti, e precisamente:

 


 

Operatore new

 

In C++, l'operatore new costruisce uno o più oggetti nell'area heap e ne restituisce l'indirizzo. In caso di errore (memoria non disponibile) restituisce NULL.

Gli operandi di new (tutti alla sua destra) sono tre, di cui solo il primo é obbligatorio (le parentesi quadre nere racchiudono gli operandi opzionali):

new   tipo   [[dimensione]]   [(valore iniziale)]

NOTA: si è potuto riscontrare che a volte i due operandi opzionali sono mutuamente incompatibili (alcuni compilatori più antichi danno errore): in pratica (vedremo perchè parlando dei costruttori), se il tipo è nativo inizializza comunque tutti i valori con zero, se il tipo è astratto funziona bene (a certe condizioni).

Ovviamente l'operatore new non può restituire un l-value; può essere invece un r-value sia nelle inizializzazioni che nelle assegnazioni, e può far parte di operazioni di aritmetica fra puntatori . Esempi:
        inizializzazione:                int
* punt = new int (7);
        assegnazione con operazione aritmetica:
                                                struct anagrafico { ....... } ;
                                                anagrafico
*   p_anag ;
                                                p_anag = new anagrafico [100] + 9 ; 

nel primo esempio alloca un oggetto int (inizializzato con il valore 7) nell'area heap e usa il suo indirizzo per inizializzare il puntatore punt; nel secondo esempio definisce la struttura anagrafico e definisce un puntatore a tale struttura, a cui assegna l'indirizzo del decimo di cento oggetti di tipo anagrafico, allocati nell'area heap.

 


 

Operatore delete

 

In C++, l'operatore binario delete (con un operando opzionale e l'altro obbligatorio) dealloca la memoria dell'area heap puntata dall'operando (obbligatorio). Non restituisce alcun valore e quindi deve essere usato da solo in un'istruzione (non essendo né un l-value né un r-value non può essere usato in un'espressione con altre operazioni).
Es.:           allocazione:      int* punt = new int ;
deallocazione:   delete punt ;

Contrariamente all'apparenza l'operatore delete non cancella il puntatore né altera il suo contenuto: l'unico effetto é di liberare la memoria puntata rendendola disponibile per ulteriori allocazioni (se l'operando non punta a un'area heap alcuni compilatori generano un messaggio di errore (o di warning), altri no, ma in ogni caso l'operatore delete non ha effetto).

Se l'operando punta a un'area in cui è stato allocato un  array di oggetti, bisogna inserire dopo delete l'operando opzionale, che consiste in una coppia di parentesi quadre (senza la dimensione dell'array, che il C++ é in grado di riconoscere automaticamente).
Es.:         float* punt = new float [100] ;       (alloca 100 oggetti float )
delete [ ] punt ; (libera tutta la memoria allocata)

     
L'operatore delete costituisce l'unico mezzo per deallocare memoria heap, che, altrimenti, sopravvive fino alla fine del programma, anche quando non é più raggiungibile.

Es.: int* punt = new int ;     (alloca un oggetto int nell'area heap e inizializza punt con il suo indirizzo)
int a ;     (definisce un oggetto int nell'area stack)
punt = &a ; (assegna a punt un indirizzo dell'area stack; l'oggetto int dell'area heap non é più raggiungibile)

[p38]

 


 

Torna all'Indice