Liste e acquisizione da file

di il
13 risposte

Liste e acquisizione da file

Salve a tutti, ho di nuovo problemi con le liste e più i particolare con l'acquisizione dei valori da file.
Ora non ho postato il contenuto del file ma ci sono 5 righe contenenti in ordine nome, cognome, matricola, materia e voto.
Fin qui sembra essere ok, nel senso che la funzione di acquisizione sembra funzionare, ma quando nel main faccio la stampa, essa termina con uno strano return (un errore quindi) e non stampa la successiva printf ("SONO QUI")..
Sono diversi giorni che mi riguardo questo programma e non capisco l'errore/gli errori (o orrori)...

#include <stdio.h>
#include <string.h>

struct nodo {
	char nome [20];
	char cognome [20];
	int matricola;
	char materia[20];
	int voto;	
	struct nodo *next;
};
typedef struct nodo ElementoDiLista; 
typedef ElementoDiLista* ListaDiElementi;


void InserisciInTesta(ListaDiElementi *testa);

int main() {
	
	ListaDiElementi Testa;
	
	InserisciInTesta(&Testa);
	ElementoDiLista* tmp = Testa;
	
	while(tmp != NULL) {
		printf("%s %s %d %s %d\n", tmp->nome, tmp->cognome, tmp->matricola, tmp->materia, tmp->voto);
		tmp = tmp->next;
		}
		
		printf("SONO QUI\n");
		
	return 0;
}

ElementoDiLista* CreaNodo() {
	ElementoDiLista* temp = (ElementoDiLista *)malloc(sizeof(ElementoDiLista));
	
	if(temp) {
		temp->next = NULL;
		return temp;
	}
	
	else printf("Errore!\n");
}

void InserisciInTesta(ListaDiElementi* testa) {	

	FILE *fp;
 	fp = fopen("input.txt", "r");
 	
 	while(!feof(fp)) {
        ElementoDiLista* temp = CreaNodo();
        fscanf(fp, "%s %s %d %s %d", &temp->nome, &temp->cognome, &temp->matricola, &temp->materia, &temp->voto);
       
	if(*testa == NULL) {
	    	*testa = temp;
		 }
	 
	    else {			
		 temp->next = *testa;
		 *testa = temp;
		}
	 }
	 
	 fclose(fp);	
}

13 Risposte

  • Re: Liste e acquisizione da file

    Perchè non hai eseguito l'inizializzazione

    ListaDiElementi Testa=NULL;

    Devi fare attenzione ... ma te ne accorgi subito ...
  • Re: Liste e acquisizione da file

    Cavolo è vero, non ci posso credere che abbia sbagliato di nuovo lì..Mi vergogno quasi ora ad aver postato questa discussione..Grazie mille comunque, adesso funziona!
  • Re: Liste e acquisizione da file

    
    void EliminaElemento(ListaDiElementi testa) {
    	while(testa != NULL && (testa->voto >= 18)) 
    	{
    		testa = testa->next;
    	}
    	
    	ElementoDiLista* nodo = testa->next;
    	
    	testa->next = nodo->next;
    	free(nodo);
    }
    
    Ciao, come continuo di quel codice ho creato questa funzione che elimina dalla lista un elemento con voto minore di 18 (appena chiarisco le idee farò la versione che elimina più elementi senza fermarsi).

    Ho un dubbio, come mai come parametro della funzione mi basta mettere "testa", mentre per l'inserimento devo fare " *testa "?
    Quando faccio "testa = testa->next" non dovrei ritrovarmi la lista del main scorsa di n elementi?
    Cioè mi chiedo come mai per inserire elementi e modificare la Testa ho usato un parametro puntatore e qui riesco a eliminare un elemento senza invece usare *testa?
    Scusami per le tante domande ma mi sto confondendo tantissimo con i puntatori, come puoi ben vedere..
  • Re: Liste e acquisizione da file

    Ma sei sicuro che quel codice faccia quello che pensi? Controllalo meglio
  • Re: Liste e acquisizione da file

    Vedo che insisti con questi typedef fuorvianti?!

    LuigiC++ ha scritto:


    Ho un dubbio, come mai come parametro della funzione mi basta mettere "testa", mentre per l'inserimento devo fare " *testa "?
    Quando faccio "testa = testa->next" non dovrei ritrovarmi la lista del main scorsa di n elementi?
    Cioè mi chiedo come mai per inserire elementi e modificare la Testa ho usato un parametro puntatore e qui riesco a eliminare un elemento senza invece usare *testa?
    Scusami per le tante domande ma mi sto confondendo tantissimo con i puntatori, come puoi ben vedere..
    Innanzitutto ti giro questo mio vecchio post se ti può essere utile:
    In C il passaggio degli argomenti ad una funzione avviene sempre per copia, ossia qualsiasi modifica ai parametri formali non avrà alcun effetto sui parametri attuali. L'unico modo quindi in cui una funzione f possa modificare una generica variabile n appartenente ad un ambito di visibilità (non globale) diverso da quello di f, è quello di accedere ad n attraverso il suo indirizzo di memoria (ossia &n). A questo punto risulta ovvio che, dovendo contenere &n, il tipo dell'argomento (chiamiamolo m) sarà un puntatore di un ordine superiore rispetto ad n.
    Per esempio:
    n = int ==> m = int*
    n = char ==> m = char*
    n = float* ==> m = float**
    n = double**** ==> m = double*****
    In ogni caso, tornando alla tua domanda, mi sembra ovvio che se la testa deve essere modificata dalla funzione, allora l'argomento deve essere di tipo nodo**, in caso contrario risulta sufficiente un argomento di tipo nodo*.
    Si deduce quindi che la funzione EliminaElemento() è sbagliata a partire dal prototipo, in quanto nel caso di eliminazione del primo elemento non hai alcun modo di aggiornare la testa della lista.

    Inoltre ti consiglio di implementare una funzione
    void elimina_nodo(nodo** p);
    oppure
    nodo* elimina_nodo(nodo* p);
    finalizzata all'eliminazione di un generico nodo, per poi utilizzarla in una funzione cancella_insufficienze().
  • Re: Liste e acquisizione da file

    Il problema è che a fine mese ho un esame di Laboratorio di programmazione, e darà una traccia dove sarà necessario usare le liste così definite (fosse per me abbandonerei i typedef):
    
    struct nodo {
    .......
    ...
    struct nodo *next
    }
    typedef struct nodo ElementoDiLista; 
    typedef ElementoDiLista* ListaDiElementi;
    
    Con lo scorso esame di programmazione(la parte "teorica" di Laboratorio), andato anche benone, il professore richiedeva questa dichiarazione:
    
    typedef struct Nodo_SL {
    int dato;
    struct Nodo_SL *next;
    } Nodo_SL;
    
    typedef struct Lista_SL {
    Nodo_SL *next;
    } Lista_SL;
    
    La confusione è nata quando mi sono andato a rivedere i vecchi esercizi che avevo sulle liste, e data la poca familiarità con i puntatori, sono andato letteralmente fuori pista.
    Ricapitolando, avendo
    typedef struct nodo ElementoDiLista;
    typedef ElementoDiLista* ListaDiElementi;
    e la dichiarazione ListaDiElementi Testa = NULL; i prototipi saranno:
    void InserisciInTesta(ListaDiElementi *testa);
    void StampaLista(ListaDiElementi testa);
    e purtroppo ancora non capisco per l'eliminazione quali devono essere..
  • Re: Liste e acquisizione da file

    Tutte le volte che puoi modificare il puntatore allora devi usare il doppio puntatore. La cancellazione è come l'inserimento. Nella Stampa non modifichi e quindi il doppio puntatore non serve.
  • Re: Liste e acquisizione da file

    Ciao! Ho finalmente completato la funzione per l'eliminazione:
    
    void EliminaElemento(ListaDiElementi *testa) {
      ListaDiElementi s, r; 
      
      if(*testa == NULL)
        return;
    
     // eliminazione dal secondo in poi //
      if((*testa)->next != NULL) {
        s = *testa; //copio il puntatore *pl in s
        while(s->next != NULL) {
          if(s->next->voto < 18 ) {
            r = s->next;
            s->next = s->next->next;
            free(r);
          }
          else
            s=s->next;
        }
      }
      
      //eliminazione della testa, se necessario
      if((*testa)->voto < 18) {
        r = *testa;
        *testa = (*testa)->next;
        free(r);
      }
     
    }
    
    Funziona, vi trovate?
    Poi nella traccia che sto seguendo è infine richiesto di riscrivere su un file la lista finale, facendo però comparire consecutivi i record con stesso nome/cognome, SENZA modificare la struttura della lista..Anche qui sto avendo qualche problema, non capisco come fare senza toccare la lista..Potrei fare una funzione che prende come parametro la lista e sfrutto la "copia" per ordinarla e scriverla nel file? C'è qualche altro modo?
    Non posso ordinare direttamente la lista originale in quanto deve rimanere com'è..
  • Re: Liste e acquisizione da file

    LuigiC++ ha scritto:


    Funziona, vi trovate?
    Ora non ho tempo di controllare il codice, ma di certo non stai sfruttando appieno i vantaggi che derivano dall'utilizzo dei puntatori doppi:
    void EliminaElemento(nodo **p)
    {
        while(*p)
        {
            if((*p)->voto < 18)
            {
                nodo *temp = *p;
                *p = (*p)->next;
                free(temp);
            }
            else
            {
                p = &(*p)->next;
            }
        }
    }
  • Re: Liste e acquisizione da file

    Nippolo ha scritto:


    LuigiC++ ha scritto:


    Funziona, vi trovate?
    Ora non ho tempo di controllare il codice, ma di certo non stai sfruttando appieno i vantaggi che derivano dall'utilizzo dei puntatori doppi:
    Ma alla fine sto lavorando con un doppio puntatore perchè se ho una dichiarazione di questo tipo:
    
    typedef struct nodo ElementoDiLista; 
    typedef ElementoDiLista* ListaDiElementi;
    
    e faccio poi
    ListaDiElementi *testa
    sto dichiarando un puntatore ad un puntatore che punta ad un nodo, o sbaglio?
    Sono semplicemente quei typedef che confondono, come già mi hai ribadito..però se ragioni a ritroso ti trovi un doppio puntatore e da quello che vedo i nostri codici sono in fine equivalenti
  • Re: Liste e acquisizione da file

    LuigiC++ ha scritto:


    Ma alla fine sto lavorando con un doppio puntatore perchè se ho una dichiarazione di questo tipo:
    
    typedef struct nodo ElementoDiLista; 
    typedef ElementoDiLista* ListaDiElementi;
    
    e faccio poi
    ListaDiElementi *testa
    sto dichiarando un puntatore ad un puntatore che punta ad un nodo, o sbaglio?
    Sì con
    ListaDiElementi *testa
    stai dichiarando un puntatore doppio, ma non sei obbligato a farlo. Nel senso che la funzione che cancella le insufficienze può essere implementata in due modi:
    void EliminaElemento(ListaDiElementi *testa);
    oppure
    ListaDiElementi EliminaElemento(ListaDiElementi testa);
    sta a te poi scegliere quale utilizzare...

    LuigiC++ ha scritto:


    Sono semplicemente quei typedef che confondono, come già mi hai ribadito..però se ragioni a ritroso ti trovi un doppio puntatore e da quello che vedo i nostri codici sono in fine equivalenti
    Quello che sto cercando di dirti è che se scegli la strada col puntatore doppio, allora tanto vale sfruttarne appieno le potenzialità... ok sia il mio codice che il tuo utilizzano i puntatori doppi e sono funzionanti, ma è evidente che quello da me postato risulta molto più semplice e conciso.
    Lo scopo del mio precedente post era appunto quello di farti capire che hai per le mani una Ferrari e la guidi come se fosse una Panda!
  • Re: Liste e acquisizione da file

    Ok grazie mille, ho capito, ho implementato la tua funzione nel mio codice e l'ho confrontata attentamente con la mia. In effetti nella mia c'è anche una notazione abbastanza appesantita e come hai fatto tu è tutto più chiaro. Riguardo al problema di riscrivere le lista su un file facendo comparire consecutivi due record con lo stesso nome hai qualche idea? Non devo toccare la struttura della lista..
  • Re: Liste e acquisizione da file

    Di niente!
    L'argomento FILE invece mi manca, quindi mi dispiace ma non posso aiutarti.
Devi accedere o registrarti per scrivere nel forum
13 risposte