![]()
Hasta la lista, amigos!di Stefano Casini
[ Il tasso del Tasso del tasso del tasso ... || Come gestire una lista || Al ristorante || Cameriere, il conto! ] [ Conclusioni ] IntroduzioneA Roma c'è un posto, sul colle Gianicolo, chiamato la quercia del Tasso. Deriva il nome
da un albero, una quercia per l'appunto, sotto la quale era facile trovare il famoso poeta
Torquato Tasso, autore della "Gerusalemme liberata", nei giorni di bel tempo. Il tasso del tasso del tasso del tasso ... typedef struct tag_ELEMENTO { char membro1; int membro2; struct tag_ELEMENTO * pPrecedente; struct tag_ELEMENTO * pSuccessivo; } ELEMENTO; typedef ELEMENTO * P_ELEMENTO; Nell'esempio abbiamo definito un tipo di dato, precisamente una struttura, chiamato
ELEMENTO, che contiene come membri un intero, un carattere e due puntatori alla
struttura ELEMENTO: uno che punterà l'elemento precedente della lista, l'altro che
punterà il successivo. Come gestire una lista void main(void) { int numeroelementi = 5; int i; int posizioneElemento = 2; P_ELEMENTO pPila = NULL; for(i = 0; i < numeroelementi; i++) pPila = AggiungiElemento(pPila, 'A' + (i % 26), i); /* creiamo la lista aggiungendo i vari elementi */ RimuoviElemento(&pPila, posizioneElemento); /* rimuoviamo uno degli elementi della pila, contando da zero partendo dall'ultimo */ CancellaLista(&pPila); /* cancelliamo tutti gli elementi della pila */ } P_ELEMENTO AggiungiElemento(P_ELEMENTO pPila, char membro1, int membro2) { P_ELEMENTO pElem; pElem = malloc(sizeof(ELEMENTO)); /* allochiamo memoria per un elemento della lista */ if(!pElem) return pPila; /* se l'allocazione fallisce, ritorniamo il puntatore alla pila */ pElem->membro1 = membro1; pElem->membro2 = membro2; pElem->pPrecedente = pPila; /* riempiamo i membri della struttura*/ return pElem; } void CancellaLista(P_ELEMENTO * ppPila) { while(* pPila) * pPila = RimuoviUltimoElemento(* pPila); /* cancelliamo uno per uno tutti gli elementi della lista */ } P_ELEMENTO RimuoviUltimoElemento(P_ELEMENTO pPila) { P_ELEMENTO pElem; if(!pPila) return NULL; /* controlliamo che ci venga passato un puntatore valido */ pElem = pPila->pPrecedente; /* salviamo nella variabile pElem il puntatore al penultimo elemento */ free(pPila); /* cancelliamo dalla memoria l'ultimo elemento */ return pElem; /* adesso pElem punta all'ultimo elemento */ } void RimuoviElemento(P_ELEMENTO *ppPila, int posizioneElemento) { P_ELEMENTO pElemCorrente; P_ELEMENTO pAppoggio; if(! * ppPila) return; /* controlliamo che ci venga passato un puntatore valido */ if(posizioneElemento == 0) { * ppPila = RimuoviUltimoElemento(* ppPila); return; } /* se l'elemento da rimuovere è l'ultimo, chiamiamo la funzione apposita */ /* adesso possono presentarsi due possibilità: l'elemento da rimuovere è contenuto nella lista, oppure la lista è più corta del previsto, per cui l'elemento non esiste */ pElemCorrente = * ppPila; /* pElemCorrente punta all'elemento su cui ci portiamo mano mano che scorriamo la lista a ritroso: iniziamo a scorrere la lista dall'ultimo elemento, naturalmente */ while(--posizioneElemento) { pElemCorrente = pElemCorrente->pPrecedente; if(!pElemCorrente->pPrecedente) return; } /* se scorrendo la lista ci troviamo che l'elemento corrente non ne ha altri precedenti, vuol dire che la lista è più corta del previsto, per cui la funzione ritorna senza fare nulla */ pAppoggio = pElemCorrente->pPrecedente; pElemCorrente->pPrecedente = pElemCorrente->pPrecedente->pPrecedente; free(pAppoggio); /* se invece l'elemento da togliere è contenuto nella lista dovremo usare una variabile di appoggio per il puntatore all'elemento da togliere. pElemCorrente->pPrecedente->pPrecedente è equivalente a scrivere pAppoggio->pPrecedente, ma fa più effetto su Eugenio */ return; } Al ristorante Visto che nell'articolo precedente (Beta 05/96) ci siamo sbizzarriti con gli array di array, adesso vediamo di aumentare la confusione di Eugenio con le liste di liste di liste. Quando entriamo in un ristorante, il nostro tavolo si aggiunge alla lista dei tavoli occupati. Se vediamo passare Eugenio e lo invitiamo a mangiare con noi (cosa che egli difficilmente rifiuta, vista la sua naturale propensione per la buona cucina), si aggiunge una persona alla lista di quelle sedute al tavolo. A sua volta, Eugenio aggiungerà molti elementi alla lista dei piatti da lui ordinati e mangiati (oltre ad assaggiare tutto quello che hanno ordinato gli altri commensali, N.d.A.). Definiamo allora la lista dei piatti che ciascuna persona ordina: typedef struct tag_PIATTO { char * piatto; int costo; struct tag_PIATTO * pPrecedente; struct tag_PIATTO * pSuccessivo; } PIATTO; typedef PIATTO * P_PIATTO; quindi la lista dei commensali seduti al tavolo: typedef struct tag_COMMENSALE { char * nome; P_PIATTO pPiattiOrdinati; struct tag_COMMENSALE * pPrecedente; struct tag_COMMENSALE * pSuccessivo; } COMMENSALE; typedef COMMENSALE * P_COMMENSALE; infine la lista dei tavoli: typedef struct tag_TAVOLO { int numerotavolo; P_COMMENSALE pCliente; struct tag_TAVOLO * pPrecedente; struct tag_TAVOLO * pSuccessivo; } TAVOLO; typedef TAVOLO * P_TAVOLO; Tralasciando per ragioni di spazio e di tempo il codice necessario per sedersi al tavolo ed ordinare, vediamo cosa succede quando diciamo: Cameriere, il conto! Se ancora non lo avete capito, il cameriere è Alberto, programmatore esperto! void main(void) { P_TAVOLO pTavolo = NULL; P_TAVOLO pNostroTavolo = NULL; int numerotavolo = 3; int conto; pTavolo = ............ /* creiamo la lista dei tavoli aggiungendo i vari elementi */ ....................... /* aggiungiamo ai tavoli la lista delle persone e dei piatti ordinati */ pNostroTavolo = CercaTavolo(pTavolo, numerotavolo); /* cerchiamo il nostro tavolo nella lista dei tavoli */ conto = CalcolaConto(pNostroTavolo); /* calcoliamo il conto per il nostro tavolo */ RimuoviTavolo(&pTavolo, pNostroTavolo); /* rimuoviamo il nostro tavolo dalla lista dei tavoli */ ............. /* quando tutti i tavoli sono stati rimossi, è ora di chiudere il ristorante */ } P_TAVOLO CercaTavolo(P_TAVOLO pTavolo, int numerotavolo) { P_TAVOLO pElemCorrente; pElemCorrente = pTavolo; while(* pElemCorrente) { if(pElemCorrente->numerotavolo == numerotavolo) break; pElemCorrente = pElemCorrente->pSuccessivo; } /* scorriamo la lista dei tavoli finché non troviamo il nostro tavolo */ return pElemCorrente; } int CalcolaConto(P_TAVOLO pTavolo) { P_COMMENSALE pCommensale; int conto = 0; pCommensale = pTavolo->pCliente; /* questo è il primo dei clienti del tavolo */ while(pCommensale) { P_PIATTO pPiatto; pPiatto = pCommensale->pPiatto; /* questo è il primo piatto ordinato dal cliente */ while(pPiatto) { conto += pPiatto->costo; pPiatto = pPiatto->pSuccessivo; /* passo al piatto successivo */ } pCommensale = pCommensale->pSuccessivo; /* passo al cliente successivo */ } return conto; } void RimuoviTavolo(P_TAVOLO * ppTavolo, P_TAVOLO pNostroTavolo) { if(* ppTavolo == pNostroTavolo) * ppTavolo = pNostroTavolo->pSuccessivo; /* se il nostro tavolo è il primo della lista, dopo averlo sganciato il puntatore all'inizio della lista dei tavoli dovrà essere spostato sul tavolo successivo */ if(pNostroTavolo->pSuccessivo != NULL) pNostroTavolo->pSuccessivo->pPrecedente = pNostroTavolo->pPrecedente; if(pNostroTavolo->pPrecedente != NULL) pNostroTavolo->pPrecedente->pSuccessivo = pNostroTavolo->pSuccessivo; /* sganciamo il nostro tavolo dalla lista, riagganciando tra loro il tavolo precedente e quello successivo, controllando che le eccezioni quando il tavolo da sganciare è il primo o l'ultimo della lista */ ........... /* liberiamo la memoria allocata per la lista dei commensali e le liste dei piatti ordinati al nostro tavolo */ free(pNostroTavolo); /* liberiamo la memoria allocata per il nostro tavolo */ } Alla fine, messo di fronte all'alternativa: o paghi il conto, o scrivi le parti di codice mancanti, Eugenio finisce per pagare per tutti, e torna a casa con la netta sensazione di non aver digerito bene. Conclusioni
Articoli già pubblicati dall'autore su BETA: Chi ha paura dei puntatori? (Beta 01/96) Allacciate le stringhe (Beta 02/96) Nei dintorni di malloc (Beta 05/96)
|
![]() |
![]() |
![]() |
![]() |
![]() |
Copyright © 1995-1997 BETA. Tutti i diritti riservati.