Newsgroup: it.comp.lang.c
Titolo: [Help!] Non riesco a trovare il bug in questo mio programma!

Salve ragazzi.
Devo fare un programma che, sfruttando le liste doppie, riesca, data
una lista contenente numeri interi come elementi, a togliere i numeri
dispari ed a lasciare quelli pari.
Per ora ho tralasciato i casi in cui ho come primo o come ultimo
elemento un numero dispari, ed ho immaginato di averli solamente in
mezzo alla lista. Questo problema lo devo risolvere ricorsivamente.
Questo è il codice e scorgendolo vedrete che in un punto il debugger
mi dice che avviene un errore di segmentazione, sebbene il compilatore
compili il programma in maniera corretta:
**
#include <stdio.h>
#include <stdlib.h>
typedef struct Numeri
    {
        int num;
        struct Numeri *prev;
        struct Numeri *next;
    } Numeri;
 
void inserisci(Numeri *lista)
    {
        Numeri *temp = lista;
        char controllo = 's';
        while (controllo == 's')
        {
           printf("\ninserisci un numero: ");
           scanf("%d", &temp->num);
           printf("\nVuoi inserirne un'altro [s/n]? ");
           scanf(" %c", &controllo);
           if (controllo == 's')
              {
                  temp->next = (Numeri*) malloc (sizeof(Numeri));
                  temp->next->prev = temp;
                  temp = temp->next;
              }
           else temp->next = NULL;
        }
        return;
    }
 
void elimina(Numeri **lista)
    {
        Numeri **prev;
        Numeri **next;
        if (((*lista)->num % 2) == 0)
            if ((*lista)->next == NULL) return;
            else return(elimina(&(*lista)->next));
        if (((*lista)->num % 2) == 1)
           if ((*lista)->prev != NULL && (*lista)->next != NULL)
                {
                    *prev = (*lista)->prev;  /* <-- Questo è il punto
in cui il debugger mi dice che si verifica l'errore di segmentazione
*/
                    *next = (*lista)->next;
                    (*prev)->next = *next;
                    (*next)->prev = *prev;
                    free(*lista);
                    return(elimina(&*next));
                }
 
    }
 
void stampa(Numeri *lista)
    {
 
    /* Ritorno al primo elemento */
        while (lista->prev != NULL)
           lista = lista->prev;
 
    /* Stampo tutti gli elementi */
 
        while (lista != NULL)
          {
            printf("Numero inserito: %d\n", lista->num);
            lista = lista->next;
          }      
        return;
    }
int main(void)
    {
        int debug;
        Numeri *lista;
        lista = (Numeri*) malloc (sizeof(Numeri));
        lista->prev = NULL;
        printf("Riconoscitore di numeri pari e dispari\n");
        inserisci(lista);
        elimina(&lista);
        if (lista != NULL)
            stampa(lista);
        else printf("La lista è vuota!");
        scanf("%d", &debug);
    }
**
Grazie per qualsiasi risposta, voglio veramente venirne fuori da
questo problema!

 */
Ciao, essendo "elimina" la funzione che ti dà l'errore, ti riscrivo
solo questa. Io personalmente non ho testato la parte restante del tuo
programma (non avrei tempo), e ho supposto che sia corretto. La mia
versione tiene conto anche del caso in cui i nodi con valore pari si
possano trovare in prima o in ultima posizione. Poiché esiste una
semplice soluzione iterativa, ho preferito mettere da parte la
dispendiosa ed inutile ricorsione. La mia funzione può, ovviamente,
essere ulteriormente perfezionata; in alcuni casi ho usato un codice
ridondante solo per essere più chiaro. Se ci sono ulteriori problemi
fammi sapere. Buon lavoro.
<CODE>
void elimina(Numeri **lista) {
    Numeri *curr = *lista, *temp;
    while (curr != NULL) {
        /* lista di un solo elemento */
        if ((curr->prev == NULL) && (curr->next == NULL)
                                 && (curr->num % 2 == 1)) {
            free(curr);
            *lista = NULL;
        }
        /* primo elemento della lista */
        else if ((curr->prev == NULL) && (curr->next != NULL)) {
            if (curr->num % 2 == 1) {
                temp = curr;
                curr->next->prev = NULL;
                curr = curr->next;
                free(temp);
                *lista = curr;
            } else curr = curr->next;
        }
        /* ne' primo ne' ultimo elemento della lista */
        else if ((curr->prev != NULL) && (curr->next != NULL)) {
            if (curr->num % 2 == 1) {
                temp = curr;
                curr->next->prev = curr->prev;
                curr->prev->next = curr->next;
                curr = curr->next;
                free(temp);
            } else curr = curr->next;
        }
        /* ultimo elemento della lista */
        else if ((curr->prev != NULL) && (curr->next == NULL)) {
            if (curr->num % 2 == 1) {
                curr->prev->next = NULL;
                free(curr);
                curr = NULL;
            } else curr = curr->next;
        }
    }
}
</CODE>



Ciao grazie della soluzione iterativa (lo so anche io che è molto +
intelligente farla iterativamente), però purtroppo mi chiedono di
farla ricorsiva sti scemotti:-((

 farla ricorsiva sti scemotti:-((
passare all'algoritmo ricorsivo è piuttosto semplice, ora che ti ho
mostrato la soluzione iterativa. Quindi non te lo posto perché un po'
dovrai pur spremerti anche tu :)
In ogni caso, nei tuoi prossimi articoli cerca di quotare meglio (è
inutile riportare tutto l'articolo precedente, che è già disponibile
sul server). Per maggiori informazioni a riguardo visita i gruppi
it.faq e it.news.aiuto ;-)
Ciao e ancora buon lavoro.
-- 
** Giuseppe **



Si ok, ci proverò :)
Cmq volevo chiederti...il puntatore curr lo usi per mantenere
l'indirizzo del primo elemento nella puntatore *lista giusto?
Però se tratto il problema ricorsivamente e comunque dopo decido di
farmi una procedura che scorre la lista finchè non arriva al primo
elemento, la cosa non diventa superflua?
E poi non sono ancora riuscito a capire quel cavolo di errore di
segmentazione che il programma mi da PROPRIO in quel punto... :(
Grazie x qualsiasi risposta!

 */
assegni a *prev un certo valore ma senza prima aver dato un indirizzo a
prev!
E' come se, avendo un puntatore singolo, volessi modificarne il valore a cui
punta senza dire a COSA punta, senza assegnarli un indirizzo!!!
Non so se mi spiego...
Ricorda che l'operatore * dereferenzia un puntatore, e con la tua
manipolazione sopra dereferenzi un puntatore non inizializzato!!!
Prova quindi a correggere l'errore rendendo **prev e **next puntatori
singoli e fammi sapere.
2. Io organizzerei il tutto in modo diverso, in modo da rendere il programma
assai più leggibile e manutenibile (oltre che aderente alle tecniche
standard di programmazione delle strutture dati); ecco come:
- creazione della struttura (e fin qui ci sei anche tu);
- creazione della funzione di inserimento in lista di un singolo nodo
indipendente dall'input dell'utente (mentre tu vi includi dentro l'input);
- creazione della funzione di rimozione di un singolo nodo indipendente dal
tipo di nodo che si vuole rimuovere (mentre tu includi tutto qui dentro,
ricerca dei nodi da rimuovere e approccio ricorsivo);
- creazione della funzione di input da parte dell'utente, che richiamerà la
funzione di inserimento in lista;
- creazione della funzione di ricerca ricorsiva dei nodi della lista da
rimuovere ed eventuale richiamo della funzione di rimozione;
- main.
Spero di essere stato chiaro e di aiuto.
Saluti,
Sergio