Puntatori, gestione dinamica della memoria e stringhe

Slides:



Advertisements
Presentazioni simili
Puntatori Linguaggio C.
Advertisements

Unità Didattica 3 Linguaggio C
Parte 3 Lo stato: variabili, espressioni ed assegnazioni
1 Elementi DI INFORMATICA Università degli Studi di Cagliari Corso di Laurea in Ingegneria Elettronica Linguaggio C A.A. 2011/2012
Fondamenti di Informatica A - Massimo Bertozzi LE FUNZIONI.
Fondamenti di Informatica A - Massimo Bertozzi I PUNTATORI.
Fondamenti di Informatica A - Massimo Bertozzi LE RAPPRESENTAZIONI CONCATENATE.
.  I tipi di dati non primitivi sono gli array, le struct e le union.  Gli array sono degli aggregati di variabili dello stesso tipo.  La dichiarazione.
Gestione della memoria
ELEMENTI DI INFORMATICA
Ereditarietà Uno dei principi della programmazione orientata agli oggetti (OOP) è il riuso Le classi dovrebbero essere progettate come componenti riutilizzabili.
© 2007 SEI-Società Editrice Internazionale, Apogeo
Introduzione al linguaggio C
Struct, enum, Puntatori e Array dinamici
Il linguaggio C Puntatori Moreno Marzolla
Il linguaggio C Strutture Moreno Marzolla
10. Programmazione Ricorsiva Ing. Simona Colucci
Array n-dimensionali e tipi di dati strutturati
Array n-dimensionali e tipi di dati strutturati
MATRICI (ARRAY) IN MATLAB/OCTAVE
Excel 1 - Introduzione.
Strutture classi e oggetti
Process synchronization
Process synchronization
L’AMBIENTE CODE BLOCKS E L’IO
Organizzazione fisica
I FILES AD ACCESSO SEQUENZIALE
TIPI PRIMITIVI TIPI STRUTTURATI
FORMULE E FUNZIONI SU EXCEL
Tipo di dato: array Un array è un tipo di dato usato per memorizzare una collezione di variabili dello stesso tipo. Per memorizzare una collezione di 7.
Vettori dinamici Definiremo la classe vector.
Programmazione e Laboratorio di Programmazione
Programmazione e Laboratorio di Programmazione
Programmazione e Laboratorio di Programmazione
Secondo Programma in C.
I FILE di dati in C#.
Copia di oggetti il costruttore di copia ha le stesse particolarità della signature di un costruttore ordinario; il primo parametro è una reference ad.
SQL per la modifica di basi di dati
I fogli elettronici Microsoft Excel.
APPUNTI SUL LINGUAGGIO C
APPUNTI SUL LINGUAGGIO C
Process synchronization
Corso di Laurea Ingegneria Informatica Fondamenti di Informatica
La struttura dei primi programma in C
APPUNTI SUL LINGUAGGIO C Esercizi su File e Alberi Binari
Le stringhe in C++ Laboratorio 26 Aprile Dott. Serena Villata
Lucidi della Pof.ssa Pazienza
APPUNTI SUL LINGUAGGIO C
APPUNTI SUL LINGUAGGIO C
APPUNTI SUL LINGUAGGIO C Allocazione dinamica della memoria
Programmazione e Laboratorio di Programmazione
Corso di Algoritmi e Strutture Dati APPUNTI SUL LINGUAGGIO C
APPUNTI SUL LINGUAGGIO C
Programmazione e Laboratorio di Programmazione
Array n-dimensionali e tipi di dati strutturati
Programmazione e Laboratorio di Programmazione
monodimensionali: Vettori bidimensionali: Matrici
Unità 1 Programmi base.
Programmazione e Laboratorio di Programmazione
Programmazione e Laboratorio di Programmazione
Programmazione e Laboratorio di Programmazione
Programmazione e Laboratorio di Programmazione
Passaggio di parametri per indirizzo
Programmazione e Laboratorio di Programmazione
Programmazione e Laboratorio di Programmazione
Array e Stringhe Linguaggio C.
PowerShell di Windows PowerShell è un shell che mette a disposizione un prompt interattivo e un interprete a riga di comando , per le sue caratteristiche.
Programmazione e Laboratorio di Programmazione
Corso di Fondamenti di Informatica
Programmazione Procedurale
Transcript della presentazione:

Puntatori, gestione dinamica della memoria e stringhe Parte 8. Come gestire direttamente l’uso della memoria di un programma Corso A: Prof. Stefano Berardi http://www.di.unito.it/~stefano Corso B: Prof. Ugo de’ Liguoro http://www.di.unito.it/~deligu

Un’immagine di una struttura dati di “Celle e Puntatori”

Indice Parte 8: Puntatori Vettori e indirizzi: operatori *,&, sharing e alias. Puntatori e passaggio dei parametri di una funzione. Allocazione dinamica della memoria: new e delete. Stringhe, records e puntatori. La gestione diretta della memoria consente di modificare la dimensione di una struttura dati dinamicamente, cioè durante l’esecuzione di un programma.

1. Vettori e indirizzi Ricordiamo che vettori consentono l’accesso “diretto” (in un solo passo di calcolo) ai propri elementi. L’indirizzo di un elemento di un vettore viene calcolato a partire dal suo indice: per accedere a v[i] chiediamo alla RAM il contenuto dell’indirizzo b+i*d. indirizzo v[i] = b + i × d 678 v v[i] b = indirizzo base = v i = indice (o “spiazzamento”) d = dimensione elemento

L’accesso a un byte in un insieme di N bytes è in un passo 1 000 001 010 011 100 101 110 111 N=23 La RAM fornisce il contenuto della memoria b + i × d in tempo proporzionale a log2(N)

Problemi nell’uso dei vettori Un vettore è identificato in C con una costante di tipo indirizzo. La dimensione di un vettore v viene fissata per sempre al momento della dichiarazione di v. Da evitare quindi, come già spiegato: int v[x]; /* PERICOLOSO: la dimensione di v e’ il valore corrente (e “casuale” se non inizializzato) di x. Inoltre cambiare x non cambia la dim. di v */ Per inserire un nuovo elemento e nel posto i in V[0..a-1] dobbiamo prima slittare di un posto tutti gli elementi dopo i di una posizione verso destra, per esempio con un ciclo, come abbiamo fatto per l’InsertSort: for (j=a;j>i;j--) v[j]=v[j-1]; v[i]=e;

Un nuovo tipo di oggetti: i “puntatori” o indirizzi Un puntatore è una variabile il cui dominio di definizione sono gli indirizzi di memoria. Il tipo di un puntatore p viene indicato con T*, dove T è il tipo contenuti nella memoria di indice p. La costante NULL indica il generico puntatore “sbagliato”, cioé indirizzo di una memoria inesistente. La sintassi di un puntatore è: <tipo>* <var.>; int* p; // puntatore a intero int *p, *q; /* abbreviazione di int* p; int* q;*/

Dereferenziazione (*) L’operatore *p o “dereferenziazione di p” restituisce il contenuto della locazione di memoria il cui indirizzo è p 025fe16 2983 p 025fe16 Se p è l’indirizzo esadecimale 025fe16, allora *p, il contenuto di p, e’ uguale a 2983. Attenzione: se p non è inizializzato allora p è “casuale”, dunque *p è il contenuto di una memoria scelta a caso, spesso inesistente o irraggiungibile. In questo caso far calcolare *p fa cadere il programma. Per es.: calcolare *NULL fa cadere il programma.

Operatore indirizzo (&) L’operatore di & (“indirizzo di”) è una sorta di inverso di *. Se x è una variabile, allora &x è l’indirizzo RAM in cui è memorizzato il valore di x: x 025fe16 2983 &x (indirizzo di x)  025fe16, mentre *&x  x  2983 è il contenuto dell’indirizzo di x, e quindi il valore della variabile x.

Esempio di uso di *, & // esempio di dereferenziazione: uso operatore & #include <iostream.h> int main() {int x = 7; int *p1, *p2; // oppure int* p1; int* p2; p1 = &x; p2 = p1; // p1, p2 sono l’indirizzo di x cout << “*p2 =“ << *p2 << endl; // stampa il valore di x cioe’ 7 cout << “p2 = “ << p2 << endl; } /* stampa il valore di p2 cioe’ l’indirizzo di x (in esadecimale) */

Condivisione (o sharing)della stessa locazione di memoria I puntatori possono condividere (in inglese si dice: to share) l’area di memoria cui puntano: int *p, *q; int n = 44; p = q = &n; p q n 44 Ogni modifica del valore di n che avvenga per assegnazione su *p si riflette su n e su *q: p e q sono come dei “sinonimi” (in inglese: “alias”) di n. Questa caratteristica viene sfruttata per modificare i valori originali dei parametri attuali, simulando il passaggio per indirizzo.

Sharing e alias Lo scopo dello sharing (condivisione) di memoria è evitare la duplicazione dei dati, soprattutto per strutture dati molto grandi. In C++ si possono definire “sharing” tra due aree di memoria anche senza i puntatori, usando una operazione int& che costruisce sinonimi (alias): int main () {int n = 44; int& rn = n; /* rn è sinonimo di n, sono la “stessa” variabile con due nomi diversi */ n--; /* se cambio n cambio anche rn perché n, rn sono la “stessa variabile” */ cout << rn << endl; } / *stampa 43, non 44 */

Vettori e puntatori in C++ Dato che in C++ un vettore è una costante di tipo puntatore, si può assegnare un vettore a un puntatore, ma non un vettore a un vettore (sarebbe come assegnare una costante a un’altra costante). int v[100]; int* p; p = v; /* il valore assegnato a p è l’indirizzo del primo elemento di v, ossia p = &v[0] */ Si può usare la notazione con gli indici per i puntatori che rappresentano vettori: p[i] // p[i] equivale a v[i]

Aritmetica dei puntatori (1) L’operazione sizeof(T) calcola, per ogni tipo T, la dimensione in bytes della memoria necessaria per contenere un generico elemento di T. int dim = sizeof(double); /* dim = 8, la dim. in bytes di double */ I puntatori sono tipati: int *p, bool *p, double *p, … : il tipo è essenziale per sapere cosa leggere/scrivere in *p, le locazioni di memoria cui punta p.

Aritmetica dei puntatori (2) L’unità minima per un puntatore di tipo T è lo spazio sizeof(tipo di p) necessario per contenere un elemento di tipo T. In C++ si possono sommare (o sottrarre) interi a un puntatore p: l’effetto è aggiungere o togliere multipli di sizeof(tipo di p). Per esempio: int *p, *q; q=p+10; /* il valore di q è uguale al valore di p+10*sizeof(int) */ A norma di quanto detto, se p punta ad un vettore v deduciamo che p+i rappresenta la variabile v[i]: p+i==&v[i]; /* ovvero: */ *(p+i)==v[i];

2. Puntatori e passaggio dei parametri di una funzione Ripassiamo il passaggio di parametri per valore con un esempio: void f(int n){ n++; } main(){int a = 0; f(a); cout << a;} a La chiamata ad f(a) e’ per valore. Essa modifica la copia n di a da 0 ad 1. La chiamata ad f(a) modifica il valore originale 0 di a: quando stampiamo a otteniamo 0 e non 1. Per convincerci, proviamo a simulare l’esecuzione: la freccia mobile rappresenta il Program Counter.

Passaggio di parametri per valore void f(int n){ n++; } main(){int a = 0; f(a); cout << a;} a n

Passaggio di parametri per valore void f(int n){ n++; } main(){int a = 0; f(a); cout << a;} a 1 n

Passaggio di parametri per valore void f(int n){ n++; } main(){int a = 0; f(a); cout << a;} a

Ripasso: il passaggio di parametri per riferimento Ora un esempio di passaggio di parametri per riferimento. void f(int& n) { n++; } main(){int a = 0; f(a); cout << a;} a La chiamata f(a) ora e’ per riferimento. Essa crea un sinonimo n di a, una variabile cioè di nome diverso, ma con lo stesso indirizzo di a. Modificando n da 0 ad 1, la chiamata modifica il valore originale di a, dato che n ed a sono la stessa cosa.

Passaggio di par. per riferimento void f(int& n) { n++; } main(){int a = 0; f(a); cout << a;} a n

Passaggio di par. per riferimento void f(int& n) { n++; } main(){int a = 0; f(a); cout << a;} 1 a n

Passaggio di par. per riferimento void f(int& n) { n++; } main(){int a = 0; f(a); cout << a;} 1 a

Passaggio di parametri per riferimento con un puntatore Il passaggio per riferimento si può simulare con un puntatore: void f(int* pn) { (*pn)++; } main(){int a = 0; f(&a); cout << a;} a Possiamo usare un puntatore pn nella chiamata f(a) per simulare una chiamata per riferimento. *pn e’ un sinonimo di a. Modificando *pn da 0 ad 1, la chiamata modifica il valore originale di a.

Passaggio di parametri usando puntatore void f(int* pn) { (*pn)++; } main(){int a = 0; f(&a); cout << a;} a pn Notiamo che nella chiamata f(&a) dobbiamo passare l’indirizzo &a di a, e non semplicemente a, e che nel corpo di f dobbiamo fare riferimento al valore *pn puntato da pn, e non a pn stesso.

Passaggio di parametri usando puntatore void f(int* pn) { (*pn)++; } main(){int a = 0; f(&a); cout << a;} 1 a pn

Passaggio di parametri usando puntatore void f(int* pn) { (*pn)++; } main(){int a = 0; f(&a); cout << a;} 1 a

3. Allocazione dinamica della memoria e puntatori Allocazione = destinazione di una certa quantità di memoria (realmente esistente e disponibile) per contenere il valore di una variabile. Tutte le variabili di un programma sono allocate dal programma stesso quando vengono dichiarate Possiamo noi stessi allocare memoria per la variabile *p durante l’esecuzione del programma, con il comando p = new (tipo di p). La memoria allocata da noi si trova in una area detta “memoria dinamica” o “heap”.

Allocazione dinamica della memoria e puntatori L’errore più comune nell’uso dei puntatori è il seguente: calcolare il valore *p puntato da p senza aver prima “allocato” p. In questo caso, calcolando *p possiamo chiedere il contenuto di una memoria inesistente e far cadere il programma. È come far calcolare il valore di *NULL.

Allocazione dinamica: l’istruzione new La memoria allocata da noi si trova in una area detta “memoria dinamica” o “heap”. int *p; p = new int; *p = 2983; p heap Memoria n.025fe16 L’istruzione new int “alloca”, cioe’ assegna al puntatore p, l’indirizzo di uno spazio di memoria realmente esistente, non in uso, e sufficente per contenere un intero. Solo a questo punto *p può essere letta e assegnata come una variabile di tipo intero: farlo prima può far cadere il programma.

Allocazione dinamica: assegnazione di *p int *p; p = new int; *p = 2983; heap Memoria n. 025fe16 p 025fe16 *p può essere assegnata solo dopo che assegnamo a p l’indirizzo di una cella realmente esistente e raggiungibile.

Allocazione dinamica: lettura e scrittura di *p int *p; p = new int; *p = 2983; heap Memoria n. 025fe16 2983 p 025fe16 *p può essere assegnata solo dopo che assegnamo a p l’indirizzo di una cella realmente esistente e raggiungibile.

Allocazione dinamica: esempi di cosa fare e di cosa non fare float* p; *p = 3.14159; /* ERRORE: p non è allocato. Assegnare p significa scrivere su una memoria di indirizzo “casuale”, spesso inesistente e questo ha effetti imprevedibili */ float x = 3.14159; float* p = &x /* OK: p usa l’allocazione già fatta per x */ float* p = new float; *p = 3.14159; /* OK: p è già allocato con la new */

Prima che ci fosse la “new”: l’istruzione “malloc” La funzione malloc (oggi meno usata rispetto alla new) ci richiede di conoscere la dimensione n in bytes dell’area di memoria di cui abbiamo bisogno, e ci restituisce un puntatore a un’area di memoria ``libera’’ (non in uso) della dimensione n richiesta, NULL se un’area di dimensione n libera non esiste. Una istruzione p = new Tipo; si esprimeva usando la funzione malloc Tipo *p; p = (Tipo*) malloc (sizeof(Tipo));

Prima che ci fosse la “new”: l’istruzione “malloc” Tipo *p; p = (Tipo*) malloc (sizeof(Tipo)); “malloc” alloca un’area di memoria con la dimensione in byte che gli chiediamo. In questo caso la dimensione dipende da Tipo ed è calcolata da sizeof. In caso di successo l’istruzione assegna il tipo Tipo all’area di memoria, e l’indirizzo dell’area al puntatore p. Se non c’è più memoria disponibile, malloc restituisce NULL, il puntatore a una memoria inesistente, che sarà il valore di p.

I puntatori e l’allocazione dinamica di un vettore Per “allocare dinamicamente un vettore v” intendiamo: posporre il momento in cui definiamo la lunghezza di v. Non è semplice: occorre dichiarare v come puntatore, e conoscere: Il tipo degli elementi di v (per es., int); il numero lun di questi elementi durante l’esecuzione. int* v, int lun; …………………; lun = 100; ……………………; v = new int[lun]; /* attenti a non calcolare v[0] prima di allocare *v: cade il programma */

I puntatori e l’allocazione dinamica di un vettore L’istruzione v = new int[lun]; alloca un vettore di lun interi, dove lun è una variabile già assegnata. Una volta allocato, il vettore non è più modificabile (ma possiamo sempre costruirne un altro e assegnarlo a v).

4. Un altro uso dei puntatori: le variabili di stringa Le stringhe sono vettori di caratteri, contenenti un carattere speciale ‘\0’, non stampabile, detto terminatore. La lunghezza di una stringa è il numero di caratteri nel vettore precedenti il primo ‘\0’. Per esempio sono stringhe le seguenti char s[] = “CIAO”; char t[MAXLUN]; char *u = “Salve mondo”; C I A O ‘\0’ … stringa s

4. Un altro uso dei puntatori: le variabili di stringa In C++ esiste un tipo String, definito come char* : typedef char* String; “typedef TipoComposto nome” definisce un nuovo tipo “nome” uguale a ”TipoComposto”

Allocazione di spazio di memoria per stringhe Se non allochiamo una stringa, può capitarci di leggere/scrivere su un indirizzo inesistente, irraggiungibile o già occupato. Per es. con char* s; cin >> s; chiediamo di inserire una stringa in un indirizzo s non allocato e probabilmente inesistente, e possiamo far cadere il programma.

Allocazione di spazio di memoria per stringhe Per evitare errori come char* s; cin >> s; è possibile, per esempio, costruire una funzione di allocazione di memoria per le stringhe, che dato un intero “len” fornisce l’indirizzo di una stringa che può contenere fino a “len” caratteri. String stralloc(int len) { String s = new char[len + 1]; return s;} // scriviamo len + 1 per far posto a ‘\0’ Ora possiamo scrivere char* s=stralloc(100); cin >> s;

Operazioni sulle stringhe: lunghezza e copia di una stringa int strlen (String s) {int n = 0; while (s[n] != ‘\0’) {++n;} return n;} // POST.restituisce la lunghezza di s int strcpy (String dest, String source) // PREC.lunghezza dest >= lunghezza source {int n=0; while (source[n] != ‘\0’) {dest[n]=source[n]; ++n} } // POST. copia source su dest.

Records (ripasso) Un record è una tupla di valori di tipi possibilmente diversi, corrispondente in matematica a un elemento di un prodotto cartesiano, rappresentato da celle contigue a cui accediamo attraverso etichette: struct <nome struttura> {<tipo1> <etichetta campo1>; ... <tipok> <etichetta campok>;} r r.num Un esempio già visto : il tipo dei razionali r.denum struct Rational {int num; int denum;} Se r ha tipo Rational, le due componenti di r si scrivono: r.num, r.denum e si indicano come nella figura a lato

Puntatori a record (o strutture) Come per i tipi di base e per i vettori, si possono definire dei puntatori a record e poi allocarli: typedef struct Rational {int num; int denum} *Pointer; Pointer p = new Rational; //p puntatore a Rational Se p è un puntatore a Rational, allora *p è un elemento di Rational, di numeratore e denominatore (*p).num, (*p).denum. Il C++ indica (*p).num, (*p).denum con le abbreviazioni: p->num, p->denum *p elemento di Rational p->num p p->denum

Un modo per definire un tipo “Vettore” Possiamo definire un unico tipo Vettore per vettori di qualsiasi lunghezza su un tipo dato (per es. Interi). “Vettore” è il tipo dei puntatori a un record “VecRec” di due campi, il primo, lun, per la lunghezza del vettore, e il secondo, vec, per l’indirizzo del vettore vero e proprio. typedef struct VecRec{int lun; int*vec;}* Vettore; //Vettore = puntatori a un record VecRec

Una rappresentazione a “celle e puntatori” per il tipo Vettore v è un primo puntatore a un record di due elementi: il primo elemento v->lun è la lunghezza di un vettore W, il secondo, v->vec è l’indirizzo del primo elemento di W. n = v->lun W = v->vec W[0] W[n-1] W = v->vec ha tipo int*, è un secondo puntatore, che punta al primo elemento W[0] di un vettore W di n elementi. La struttura “Vettore” ha n+3 celle e 2 puntatori.

Come allocare un nuovo oggetto di tipo Vettore Supponiamo di volere un nuovo oggetto di tipo Vettore, di lunghezza n. Per predisporre uno spazio di memoria adatto a contenerlo, dobbiamo usare due volte la “new”, per definire i due puntatori (le due “frecce”) che compaiono nella rappresentazione della pagina precedente, e le celle a cui puntato tali frecce. A tal fine nel prossimo lucido definiamo una funzione: Vettore VettAlloc (int n) {… }

Come allocare un nuovo oggetto di tipo Vettore Vettore VettAlloc (int n) { Vettore v; v = new VecRec; /*new 1: alloco spazio per le 2 celle “lun”, “vec” di un record di tipo VecRec. */ v->lun = n; /* assegno n alla prima cella del record */ v->vec = new int[n]; /*new 2: alloco lo spazio per n celle di interi */ return v; }

Un esempio più complesso: il tipo “Matrice” (matrici dim. qualsiasi) struct MatrRec {int righe, colonne; int **vecrighe;}; typedef MatrRec* Matrice; Questa definizione costruisce la complessa struttura che vedete disegnata qui sotto: M:Matrice M p = r righe di c elementi ciascuna: (int*)* p M*:MatrRec W0 =p[0] Wr-1 =p[r-1] W0[0] … W0[c-1] r = M->righe c=M->colonne r p=M->vecrighe Wr-1[0] … Wr-1[c-1]

Un esempio più complesso: il tipo “Matrice” (matrici dim. qualsiasi) Per costruire uno spazio di memoria adatto a contenere una matrice, dobbiamo procedere cella per cella, e usare r+2 volte la “new”, per definire gli r+2 puntatori e le (r*c+r+4) celle del disegno, partendo dalla sola cella che contiene M. M:Matrice M p = r righe di c elementi ciascuna: (int*)* p *M:MatrRec W0 =p[0] Wr-1 =p[r-1] W0[0] … W0[c-1] r = M->righe c=M->colonne r p=M->vecrighe Wr-1[0] … Wr-1[c-1]

Come allocare un nuovo oggetto di tipo Matrice Matrice NuovaMatrice (int r, int c) { Matrice M; int i; M = new MatrRec; /* prima new: il record *M */ M->righe = r; M->colonne = c; M->vecrighe = new int*[r]; /* seconda new: il vettore p delle r righe della matrice */ for (i = 0; i < r; i++) M->vecrighe[i] = new int[c]; /* ultime r new: le r righe p[0], …, p[r-1] della matrice */ return M;} /* valore di ritorno = indirizzo del record per una matrice r x c, con gli elementi della matrice ancora “indefiniti” */

Stampa di un oggetto di tipo Matrice (M->vecrighe) è il vettore delle righe, la riga numero I è (M->vecrighe)[i], e infine (M->vecrighe)[i][j] è l’elemento num. j della riga num. i. Il numero delle righe è uguale a (M->righe) e il num. delle colonne è (M->colonne). Possiamo assegnare tutti gli elementi di M con il ciclo: for(int i=0;i<M->righe;++i) for(int j=0;j<M->colonne;++j) (M->vecrighe)[i][j] = (i+1)*(j+1);

Stampa di un oggetto di tipo Matrice Possiamo stampare tutti gli elementi di M con il ciclo: for(int i=0;i<M->righe;++i) for(int j=0;j<M->colonne;++j) {cout << "(M->vecrighe)[" << i << "][" << j << "] =" << (M->vecrighe)[i][j] << endl;}

Deallocazione: come riciclare una cella di memoria che non serve più La memoria dinamica allocata ma non più in uso può essere resa riciclabile per risparmiare spazio. A volte riciclare può essere indispensabile, quando si sono allocate così tante strutture dati che non resta più spazio per costruirne di nuove. Quando ricicliamo una cella di memoria, il compilatore la considera di nuovo “libera” e può riutilizzarla per costruire una nuova struttura. Il comando per riciclare una cella di cui sia noto l’indirizzo è delete: delete <puntatore>

Deallocazione: come riciclare una struttura dati Per deallocare un vettore del C non occorre ricordarne la dimensione, ci basta ricordarne il nome e scrivere: delete [] <nome vettore> Per deallocare una struttura complessa definita da noi, per esempio di tipo Matrice, occorrono tante chiamate di delete quante sono state quelle di new. Le delete vanno fatte percorrendo la struttura in ordine opposto rispetto al percorso fatto per costruirla: eliminiamo prima le ultime memorie allocate e continuiamo fino ad eliminare le prime.

Esempio: Deallocazione di Matrice void DeallocaMatrice (Matrice m) { int i; //(1) deallochiamo un vettore-riga dopo l’altro for(i = 0; i < m->righe; i++) delete [] m->vecrighe[i]; //(2) deallochiamo l’elenco dei vettori-riga delete [] m->vecrighe; // (3) deallochiamo l’indirizzo del record Matrice delete m;}