Sei sulla pagina 1di 4

LISTE

• Una Lista, a differenza di un array, ha un numero variabile di elementi


ed è tale da allocare, in ogni istante, un quantitativo di memoria pari a
quella minima necessaria

struct Elemento {
int valore;
struct Elemento* prossimo;
};
typedef struct Elemento* Lista;

• La definizione di lista coincide con quella di un indirizzo di un elemento


(il primo elemento della lista stessa, collegato al secondo, che è
collegato al terzo e così via)
• prossimo è un puntatore all’elemento successivo della lista
• L’ultimo elemento della lista punterà ad un elemento inesistente,
rappresentato dalla costante NULL (solitamente pari a 0, valore
sicuramente non utilizzabile per un indirizzo)

Nei nostri tipi strutturati noi abbiamo finora preso in considerazione il caso dei tipi scalari che quindi hanno
un tipo , un valore e un attributo, poi abbiamo considerato i dati strutturati che sono dati che hanno più
valori: il primo caso studiato sono gli array che hanno un solo tipo, un solo attributo e tanti valori(in
quantità prefissata)abbiamo detto che quando abbiamo un’array di dimensione N noi possiamo gestire
sia una sequenza di N dati, sia sequenze più piccole ma non più grandi.
Poi abbiamo detto che se i dati non sono omogenei ma sono eterogenei (di tipi diversi) facciamo uso delle
struct, però la quantità di dati in una struct è prefissata poiché definita proprio al momento della
dichiarazione . Il caso che ci manca è quello di dati omogenei o eterogenei in quantità variabile(ovvero
modificabile), inoltre dobbiamo fare in modo di occupare istante per istante il quantitativo giusto di
memoria questa soluzione è data dalle liste

Una lista è una sequenza di dati. La lunghezza della lista non è nota all'inizio e può variare liberamente
durante l'esecuzione del programma. 

 Le liste ci forniscono una soluzione per la riduzione di spreco di memoria nei casi in cui noi non sappiamo
quanto spazio ci servirà in memoria . La lista , siccome non sappiamo quanto essa sarà lunga all’inizio e al
contempo non vogliamo sprecare spazio in memoria, allora può essere inizializzata a NULL che sta ad
indicare proprio che la lista è vuota(viene solo memorizzata solo dove inizia anche se non inizia davvero
infatti per questo ci mettiamo NULL). Poi ammettiamo di memorizzare un numero nella listaandiamo alla
casella (142) (indirizzo) e memorizziamo il numero 1ora succede che per sapere che la nostra lista
comincia alla casella (142) dobbiamo ricordarci dove comincia. Quindi ci prenderemo una variabile, che
memorizziamo nella casella in memoria (1000), che si ricorda dove comincia la nostra lista(nel nostro caso
nella casella 1000 metteremo (memorizzeremo) 142 che ci dice che la nostra lista ha un elemento nella
casella 142).

ADESSO COME FACCIAMO A DIRE CHE LA NOSTRA LISTA POSSIEDE UN SOLO ELEMENTO?
Purtroppo alla casella 1000 non ritroviamo questa informazione poiché la casella 1000 ci dice solo
l’indirizzo di dove sta il primo elemento della lista. Se non ci sono altri elementi allora dobbiamo mettere da
qualche parte l’indicazione che non ci sono altri elementiquesta indicazione viene messa nella casella
(144)  Perché non nella 1002? Perchè il nostra lista si trova nell’heap e non nello stackinoltre se
l’avessimo messa nello stack significava che quando noi andavamo a dichiarare un puntatore che sta in
1000 dovevamo dire che non volevamo un puntatore ma due puntatori perché potrebbe avere un
elemento, solo se la nostra lista aveva 100 elementi noi avremmo dovuto dichiarare 100 puntatori quindi
significa che avremmo dovuto dichiarare tanti puntatori quanti sono gli elementi della lista quindi
dovevamo sapere in anticipo quanti puntatori avevamo bisogno(e di conseguenza quanto è lunga una
listaarray)nel modo adottato invece stiamo andando dinamicamente a metterci gli elementi

Lista vuota 
struct Elemento { 
int valore; 
struct Elemento* prossimo;

Per definire una lista noi abbiamo bisogno di un puntatore che rappresenta sostanzialmente un indirizzo del
primo elemento della listase la nostra lista è vuota per indicare questo aspetto il mdo

 
struct Elemento* lista = NULL (in questo modo il compilatore crea una variabile nello stack che si chiama
lista che è l’indirizzo di una struct Elemento e che all’inizio vale NULL)
 
           
           
           
           
(1000) NULL          
 
La variabile nella casella 1000 ci dice che la lista inizia a NULL, cioè non inizia proprio (è vuota) 
 

Lista con un elemento 


 
struct Elemento* lista = NULL; --> viene allocata la variabile lista nello stack nella casella 1000 
 
lista = new struct Elemento; --> crea una nuova struct Elemento nell'heap a partire dalla casella 142 e scrive
142 nello stack nella casella 1000 
 
(*lista).valore = 1; --> va nella struct Elemento della casella 142, trova il campo valore e ci scrive 1 
 
(*lista).prossimo=NULL; --> va nella struct Elemento della casella 142, trova il campo prossimo e ci scrive
NULL 
 
Per semplificare potevamo anche scrivere 
 
lista->valore=1; 
lista->prossimo=NULL; 
 
(142) 1  (144) NULL    
       
       
(1000) 142      
 
Il 142 nella casella 1000 ci dice che la nostra lista ha un elemento nella casella 142 
 

Lista con due elementi 


(142) 1  (144) 246    
    (246) 13 (248) NULL 
       
(1000) 142      
Se noi vogliamo aggiungere un nuovo elemento alla lista precedente: allora ad esempio aggiungiamo 13
che viene posizionato alla casella con indirizzo (246) (perché gli elementi della lista non per forza in
memoria sono disposti uno di seguito all’altro) ma oltre a questo dobbiamo andare a memorizzare la
posizione del secondo elemento in memoria (indirizzo) che viene messo nella casella (144) in cui
metteremo (246). Poi dopo ancora seguirà (visto che la lista nel nostro caso è formata da due elementi) alla
prossima casella in memoria (248 nel caso nostro) il valore NULL proprio per dire che finisce dopo l’
elemento 13
 
Lista con tre elementi 
(142) 1  (144) 246     
(204) 71  (206) NULL (246) 13 (248) 204 
       
(1000) 142      
 
QUESTA STRUTTURA ADOTTATA SI CHIAMA LISTA CONCATENATA OPPURE LINKEDLIST OPPURE LISTA
SINGOLARMENTE CONCATENATA 
 

COME QUESTO CONCETTO CHE ABBIAMO APPENA NARRATO PUO’ DIVENTARE UN ALGORITMO? Iniziamo col
dire che un per elemento di una lista non dobbiamo solo dire il valore ma dobbiamo anche indicare la posizione del
successivo quindi scriveremo

struct Elemento { 
int valore;  //possiamo anche mettere un qualsiasi altro tipo
struct Elemento* prossimo; //In questo modo mettiamo l’indirizzo del prossimo elemento che sarà di
tipo  struct Elemento* prossimo; che sta a significare che un elemento è fatto di un valore, come quelli
evidenziati in viola e poi è fatto di variabili di tipo indirizzo evidenziati in marrone che ci dicono la
prossima coppia valore-indirizzo

Quindi una struct Elemento è una coppia che comprende sia il valore che il puntatore e questo puntatore è
per l’appunto un puntatore ad una struttura analoga a quella che stiamo definendoma questa cosa non
è vietata? Non è vietato esprimere una struttura in funzione di se stessa? Questa cosa infatti è vietata ma
in tal caso non stiamo definendo una struttura in funzione di se stessa ma di una struttura in funzione di un
intero e di un puntatore ad struttura

Ciò non è vietato perché al compilatore interessa quanto spazio occupa la struttura in memoria e come
deve rappresentare i suoi valori 
struct Elemento* occupa in memoria lo spazio necessario per un indirizzo (una macchina a 64 bit sarà
8byte), indipendentemente dal fatto che sia un puntatore proprio a struct Elemento ed è rappresentato
come tutti gli indirizzi come un intero senza segno 

Con questa struttura noi possiamo definire tutta lista perché manca l’indirizzo del primo elemento che si
trova nello stack

Potrebbero piacerti anche