Newsgroup: it.comp.lang.c
Titolo: Liste concatenate: AIUTO!

Sono un ragazo di 19 anni che sta cercando disperatamente di imparare il C,
sto leggendo la guida completa al c dell McGraw Hill e mi sono inbattunto
nelle liste concatenate, in particolare la funzione per aggiungere un
elemento: non sono riuscito a capire perché i puntatori start, che punta
all'ininizio della lista e il puntatore lst, che punta alla fine, sono
puntatori a puntatore. Ringrazio anticipatamente chiunque mi possa dare una
mano.
struct LS_address{
 char name[40];
 char street[40];
 char city[40];
 char state[40];
 char zip[5];
 struct LS_address *next;
 };
void sls_store(struct LS_address *i, /* nuovo elemento da inserire */
   struct LS_address **start,  /* inizio della lista */
   struct LS_address **last)   /* fine della lista   */
{
 struct LS_address *old,*p;
 p = *start;
 if(!*last){ /*primo elemento della lista*/
  i->next = NULL;
  *last =i;
  *start =i;
  return;
  }
 old = NULL;
 while(p){    /* finch, c'S p onfronta il campo  */
  if(strcmp(p->name,i->name)<0){      /* nome fino a quando non trova un */
   old = p;     /* elemento minore di p->name,     */
   p = p->next;     /* altrimenti passa a esaminare    */
  }         /* l'elemento successivo           */
  else{
  if(old){/*elemento mediano*/
   old->next=i;
   i->next=p;
   return;
  }
  i->next=p; /*primo elemento*/
  *start=i;
  return;
  }
 }
 (*last)->next =i;/*ultimo elemento*/
 i->next = NULL;
 *last=i;
 }
--
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~
Filosganga
E_Mail: Filosga...@Flashnet.it
E_Mail: 
Filos...@interpuntonet.it
ICQ: #28861192
~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~~

puntatori a puntatore.
Normalmente useresti dei puntatori a struttura, ma visto che in C i
parametri delle funzioni sono passati solo per valore: è impossibile
quindi modificarli se non si utilizzano dei puntatori, in quanto non
posso modificare il puntatore, ma posso modificare l'oggetto puntato.
Se l'oggetto che voglio modificare è un puntatore, è ovvio che per
modificarlo debba utilizzare... un puntatore (non modificabile) a
puntatore (modificabile).
Se avessi utilizzato questa struttura direttamente dal main, o nella
stessa funzione in cui dichiaro la variabile di tipo struct, avrei
semplicemente dovuto utilizzare dei puntatori a lista.
Sperando di essere stato chiaro (nella foga di scrivere).
Paolo

all'ininizio della lista e il puntatore lst, che punta alla fine, sono


mano.
Non c'e' alcun motivo particolare per cui "start" e "last" debbano essere 
puntatori a puntatori, anzi parti dall'idea molto piu' intuitiva che essi sono 
semplicemente dei puntatori alla struttura "LS_address".
Se tu dovessi scrivere un programma che, all'interno della funzione main(), 
aggiunge elementi alla lista non avresti bisogno di altro!
Ora, pero', considera come vengono gestite normalmente le liste e per 
semplificare diciamo che c'e' solo il puntatore "start". All'inizio del 
programma la lista e' vuota e tipicamente a "start" si assegna il valore NULL.
Al momento di aggiungere il primo elemento bisogna tenere conto del fatto che 
"start" non punta a niente e questo caso deve essere gestito appositamente con 
qualcosa del tipo "if( start == NULL ) ...", ma una volta che il primo 
elemento e' stato aggiunto abbiamo ora che "start" punta proprio al primo 
elemento della lista. Questo e' il punto chiave: il valore del puntatore 
"start" __puo' cambiare__ man mano che si aggiungono elementi alla lista.
Come dicevo prima se restassimo sempre all'interno di main() non ci sarebbero 
problemi, ma chiaramente per fare le cose in modo pulito vogliamo scrivere una 
apposita funzione che serve ad aggiungere un elemento alla lista. Che questa 
funzione abbia bisogno del puntatore "start" e' ovvio... ma abbiamo appena 
visto che il contenuto del puntatore "start" puo' anche cambiare! Quindi 
bisogna trovare un modo per ritornare al chiamante il __nuovo__ valore di 
"start" ed e' questo il motivo per cui trovi il puntatore a puntatore: per 
cambiare il valore di "start".
Ora se ci fosse un solo puntatore in gioco sarebbe anche possibile che la 
funzione ritorni il nuovo valore, nel qual caso non avresti bisogno di usare 
il puntatore a puntatore, pero' nel nostro caso si hanno i due puntatori 
"start" e "last" e quindi bisogna ricorrere all'altra soluzione.
Spero di essere stato chiaro, tieni presente che in tutta sincerita' non ho 
controllato i dettagli della tua implementazione e il mio discorso ha 
carattere generale.


 puntatori a puntatore. Ringrazio anticipatamente chiunque mi possa dare
una
E' solo per evitare di usare variabili globali all'interno delle funzioni.
Pensaci: se non facessi cosě come faresti ad aggiornare i puntatori
all'inizio e alla fine della lista?
Personalmente preferisco usare variabili globali, e solo il puntatore
all'inizio della lista, ma su un libro credo sia piů giusto spiegare il
metodo "difficile".
--
Alessandro Monti

mano.
Il problema è questo : dato che i puntatori sono variabili come tutte
le altre, essi sono sottoposti alle stesse regole di tutte le altre :
il loro passaggio alle funzioni avviene SOLO per valore e mai per
riferimento (il C, difatti, a differenza del Pascal, non supporta il
passaggio dei parametri per riferimento).
Questo è il motivo per cui non è possibile modificare il valore
originale dei parametri passati per valore, in quanto non si
modificano i valori nelle loro effettive locazioni di memoria, ma solo
le loro immagini nello stack, quando vengono passati.
Il passaggio per riferimento, in C, viene simulato mediante l'uso di
un puntatore alla variabile ; in questo modo non viene passato il
valore della variabile per copia alla funzione, ma viene copiato il
suo indirizzo nello stack.
All'interno della funzione sarà possibile, poi, modificare il valore a
cui punta quell'indirizzo.
Stabilito questo, lasciami sfogare circa il codice che hai postato.
A me sembra che Herbert Schildt provi un gusto sadico nel complicare
la vita ai programmatori..
Io ti ho riscritto la funzione in modo + leggibile ( a mio parere ) e
più comprensibile.
<CODE>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define GT(x,y)  ( strcmp( (x), (y) ) > 0 )
#define LTE(x,y) ( ! GT(x,y) )
typedef struct address_tag {
                  char name[40] ;
                  struct address_tag * next ;
               } address_type ;
typedef address_type * address_ptr ;
void register_address( address_type info,
                       address_ptr * head,
                       address_ptr * tail )
{
     address_ptr p, temp, cursor = *head, oldcursor = cursor ;
     p = malloc( sizeof *p ) ;
     strcpy( p->name, 
info.name
 ) ;
     p->next = NULL ;
     while ( ( cursor != NULL ) &&
             ( GT ( p->name, cursor->name ) ) )
     {
           oldcursor = cursor ;
           cursor = cursor->next ;
     }
     if ( oldcursor == NULL )
     {
        *tail = *head = p ;
     }
     else
     if ( LTE( oldcursor->name, p->name ) )
     {
        temp = oldcursor->next ;
        oldcursor->next = p ;
        p->next = temp ;
        if ( temp == NULL )
        {
           *tail = p ;
        }
     }
     else
     {
        p->next = cursor ;
        *head = p ;
     }
}
void read_info ( address_type * info_ptr )
{
     printf( " name ? " ) ;
     fgets( info_ptr->name, sizeof info_ptr->name, stdin ) ;
}
void write_addresses( address_ptr head, address_ptr tail )
{
     address_ptr cursor = head ;
     printf( "\n" ) ;
     while ( cursor != NULL )
     {
           printf( "name : %s", cursor->name ) ;
           cursor = cursor->next ;
     }
     printf( "\nFirst name : %sLast name  : %s", head->name,
tail->name ) ;
}
int main()
{
    address_type AddressInfo ;
    address_ptr  Head = NULL, Tail = NULL ;
    int i ;
    for ( i = 1 ; i <= 10 ; i++ )
    {
        read_info( &AddressInfo ) ;
        register_address( AddressInfo, &Head, &Tail ) ;
    }
    write_addresses( Head, Tail ) ;
    return 0 ;
}
</CODE>
--------------------------------------------------------
* Andrea Laforgia        (
andrea_...@bigfoot.com
) *
--------------------------------------------------------

metodo "difficile".
Questione di stile.
Dal mio punto di vista l'utilizzo delle variabili globali è sempre da
evitare, per quanto possibile.
Io credo che sia sempre meglio parametrizzare le funzioni.
Utilizzando delle variabili globali, difatti, si riscontrerebbe la
difficoltà di gestire due o più liste (o comunque di ripetere
l'operazione)con un'unica funzione. 
Non credo, poi, che il metodo proposto dal libro sia da definire
"difficile", è solo che QUEL libro lo rende piuttosto complicato da
capire.


 l'operazione)con un'unica funzione.
Si, ma nei miei progetti avevo poche liste (di norma una sola), e
implementando "a mano" le funzioni si potevano fare ottimizzazioni più
spinte, tipo inserimento in ordine, ricerche "mirate", ecc, per cui comunque
la riusabilità del codice era quantomeno difficile.
In realtà da parecchio tempo, usando il C++, creo classi combinate in lista
"auto-aggreganti", per cui mi basta creare l'elemento e tenere in memoria
solo il puntatore al primo elemento, ignorando (quasi) del tutto
l'implementazione nei passi successivi.
Con i template sarebbe ancora meglio, ma sono usciti dopo, per cui mi sono
arrangiato in altri modi :-)
--
Alessandro Monti