Newsgroup: it.comp.lang.c
Titolo: Doppi puntatori (puntatori a puntatori)

Ciao a tutti, sono uno studente di ingegneria e scrivo in questo
group
per un dubbio che mi assale... Molto spesso mi è capitato di avere a
che fare con le liste e con i puntatori, e fin qui tutto ok. Mettiamo
che, data una lista di interi (non vuota e ben costruita), si deve
creare una funzione che elimini tutti gli elementi pari contenuti in
essa. Mi è stato insegnato che è necessario utilizzare i doppi
puntatori. Ma perchè??? Se uso i puntatori singoli, cosa cambia??? La
funzione SCRITTA DAL PROFESSORE è la seguente:
typedef struct list_tag {
        int value;
        struct list_tag *next;
        } list;
void DelEven(list **lpp){
   list *tmp_lp;
   while (*lpp)
      if ((*lpp)->value %2) //è dispari, avanzo
         *lpp=(*lpp)->next;
      else {//elimino
         tmp_lp=*lpp;
         *lpp=(*lpp)->next;
         free(tmp_lp);
      }
}
Ora, questa funzione non funziona proprio per niente (e specifico che
era un testo d'esame), in quanto si, elimina gli elementi pari, ma al
loro posto vengono inseriti numeri casuali, mentre la lista dovrebbe
essere "pulita", cioè gli elementi vanno eliminati, ma agli elementi
precedenti (se esistenti) vanno ricollegati gli elementi successivi a
quelli cancellati.
A parte che il programma non funziona, ma ho notato che se utilizzo i
puntatori singoli, il risultato è identico!!
typedef struct list_tag {
        int value;
        struct list_tag *next;
        } list;
void DelEven(list *lp){
   list *tmp_lp;
   while (lp)
      if (lp->value %2) //è dispari, avanzo
         lp=lp->next;
      else {//elimino
         tmp_lp=lp;
         lp=lp->next;
         free(tmp_lp);
      }
}
Qualcuno mi spiega gentilmente quando vanno utilizzati i doppi
puntatori e a cosa servono???
Grazie anticipatamente.

 puntatori e a cosa servono???
Nel tuo esempio, passi by-reference il puntatore al primo elemento della
lista in quanto anche quel primo elemento potrebbe avere un valore pari,
e quindi devi cancellarlo e passara il puntatore al nuovo primo elemento
al chiamante della funzione.
Le implementazioni erano sbagliate entrambe, in quanto non cambiano il
"next" del elemento prima di quello cancellato.
typedef struct list_tag
{
    int value;
    struct list_tag *next;
} list;
void del_even( list** lp )
{
    list* tmp;
    /* caso speciale: primo elemento della lista */
    while( *lp != NULL && !( (*lp)->value %2 ) )
    {
        tmp = *lp;
        *lp = tmp->next;
        free( tmp );
    }
    if( *lp == NULL )
    {
        return;
    }
    /* resto della lista */
    while( (*lp)->next != NULL )
    {
        if( (*lp)->next->value % 2 )
        {
            *lp = (*lp)->next;
        }
        else
        {
            tmp = (*lp)->next;
            *lp = tmp->next;
            free( tmp );
        }
    }
}
hth,
	Benjamin

 al chiamante della funzione.
Ciao, innanzitutto grazie per la risposta. Quindi tu mi dici che il
puntatore va passato per riferimento perchè altrimenti se il primo
elemento fosse pari perderei il puntatore alla lista? Ma se non uso i
doppi puntatori, non ne potrei creare uno d'appoggio prima di
eliminarlo e nel frattempo scorrere con l'originale e fare la free di
quello d'appoggio?
Poi un'altra cosa: se io nel main ho il puntatore alla lista già
creata che punta al primo elemento (lp) e passo alla funzione lo
stesso puntatore con l'istruzione:
del_even(lp);
/*nella funzione ci sarà    void del_even(list*lp){...*/
Quando poi nella funzione sposto il puntatore, mi cambia anche il
puntatore del main? E se usassi il doppio puntatore?
Grazie

 E se usassi il doppio puntatore?
Allora si che lo cambia. E perciò hai trovato anche un errore nel codice
della mia ultima risposta. Bisogna usare un'altro puntatore per scorrere
la lista.
Benjamin