Pagina 1 di 2 1 2 ultimoultimo
Visualizzazione dei risultati da 1 a 10 su 12
  1. #1

    [C] Problema con liste concatenate

    Salve a tutti, seguo questo forum da molto tempo e mi è sempre stato molto ultile, questa è la prima volta che scrivo un messaggio! così colgo l'occasione per ringraziare gli utenti

    Ora vengo al dunque:

    Mi è stato posto il problema di creare un programma che funzioni da catalogatore per una biblioteca.

    E' richiesto far uso delle liste concatenate per contenere i dati di ogni singolo libro e di dare la possiblità all'utente di inserire, eliminare, e ricercare certi libri in base a parole chiave. Penso di essere a buon punto, però si verifica un'errore che non riesco proprio a capire. Vi posto la parte essenziale del codice e poi torno a commentare in basso:

    (Ho eliminato le funzioni di ricerca perchè non possono influire sul problema, si verifica anche non utilizzandole affatto)

    codice:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define SIZE 10
    
    typedef struct Libro *lista;
    typedef struct Libro{
    	int ID;
    	char Autore[255];
    	char Titolo[255];
    	char Parole[255];
    	lista next;
    } Libro;
    
    Libro * newLibro(void);					// Restituisce un puntatore ad un nuovo libro allocato.
    void makeMenu(int *select);			        // Stampa il menu e acquisice la scelta.
    void destLibri(Libro *p);					// Dealloca tutta la lista.
    void stampLibri(Libro *p);					// Stampa la lista completa.
    void remLibri(Libro *p, int nLibri);			        // Rimuove il libro selezionato dall'elenco.
    void add2tail(Libro *p, Libro *new);			// Aggiunge un elemento in coda.
    int countlibri(Libro *p);						// Conta gli elementi della lista.
    
    int main(int argc, char *argv)
    {
    	int select, nLibri, mod;
    	Libro *inizio = (Libro *) malloc(sizeof(Libro));
    	Libro *p;
    	
    	do{ //cicla fino a quando l'utente decide di terminare.
    		makeMenu(&select);
    		while (getchar() != '\n');
    		
    		switch(select){
    			case 0: //exit
    				break;
    			case 1: //alloca libro, lo accoda e aggiorna nLibri
    				if(nLibri<SIZE){
    					p = newLibro();
    					add2tail(inizio, p);
    					nLibri = countlibri(inizio);
    					printf("Libro inserito correttamente(%d/%d).\n", nLibri, SIZE);
    				}else
    					printf("Lo spazio sugli scaffali della biblioteca è esaurito.\n");
    				break;
    			case 2: //stampa lista
    				if(nLibri!=0)
    					stampLibri(inizio->next); 
    				else
    					printf("Non ci sono libri.\n");
    				break;
    			case 5: //rimuove un libro dalla lista, aggiorna nLibri
    				if(nLibri!=0){
    					remLibri(inizio, nLibri);
    					nLibri = countlibri(inizio);
    					printf("Libro rimosso con successo(%d/%d).\n", nLibri, SIZE);
    				}else
    					printf("Non ci sono libri.\n");
    				break;
    		}
    	}while(select != 0);
    	destLibri(inizio);
    return 0;
    }
    
    void makeMenu(int *select)
    {
    	printf("\t\t==M E N U==\n"
    		   "0- Esci.\n1- Inserisci Libro.\n2- Visualizza Libri.\n3- Ricerca libri(1+ matching).\n4- Ricerca libri(full matching).\n5- Rimuovi libro.\n>");
    	scanf("%d", select);
    }
    
    Libro * newLibro(void)
    {
    	Libro *p;
    	p= (Libro *) malloc(sizeof(Libro));
    	
    	printf("Inserire l'ID del libro: ");
    	scanf("%d", &p->ID);
    	while (getchar() != '\n'); //per pulire il buffer dopo scanf
    	printf("Inserire l'Autore: ");
    	fgets(p->Autore, 255, stdin);
    	printf("Inserire il Titolo: ");
    	fgets(p->Titolo, 255, stdin);
    	printf("Inserire le 5 parole chiavi, separate da spazi: ");
    	fgets(p->Parole, 255, stdin);
    	return p;
    }
    
    int countlibri(Libro *p)
    {
    	int i = 0;
    	while(p->next != NULL ){ //<<SI BLOCCA QUI
    		i++;
    		p = p->next;
    	}
    	return i;
    }
    
    void add2tail(Libro *p, Libro *new)
    {
    	while(p->next) p = p->next;
    	p->next = new;
    }
    
    void destLibri(Libro *p)
    {
    	if(p == NULL) 
    		return;
    	Libro *temp;
    	temp = p->next;
    	free(p);
    	destLibri(temp);
    }
    
    void stampLibri(Libro *p)
    {
    	int i = 1;
    	while(p){
    		printf("========\nLibro (%d/%d):\nID: %d\nAutore: %sTitolo: %s========\n", i, SIZE, p->ID, p->Autore, p->Titolo);
    		p = p->next;
    		i++;
    	}
    }
    
    void remLibri(Libro *p, int nLibri)
    {
    	Libro *temp;
    	int i, n;
    	stampLibri(p->next);
    	do{
    		printf("Inserisci il numero in elenco del libro da rimuovere(1/%d): ", nLibri);
    		scanf("%d", &n);
    	}while(n<1 || n>nLibri);
    	
    	
    	for(i=0; i < n-1; i++) p = p->next; //scorre fino al libro prima di quello da eliminare
    	temp = p->next;
    	p->next = temp->next;
    	free(temp);
    }
    Vi spiego brevemente quello che accade:

    Dopo aver elminato un'elemento dalla lista (che non sia l'ultimo), quando vado a creare un nuovo elemento. la funzione countlibri entra in un loop infinito, e pur sapendo dov'è il problema non capisco proprio cosa lo causi!
    Ho analizzato attentamente la funzione remLibri, ma "su carta" il problema non dovrebbe esserci.(almeno credo)

    Questa è la prova su carta che ho fatto:

    Ipotizziamo una lista composta da:
    inizio->libro1->libro2->NULL

    così concatenata:
    Inizio->next = libro1
    libro1->next = libro2
    libro2->next = NULL

    Il for all'interno della funzione non esegue nulla (i=0 ; n-1=0), quindi siamo fermi ad "Inizio". proseguento passo a passo:
    - temp punta a libro1 (quello da eliminare)
    - inizio punta al next di libro1 (libro2)
    - libero lo spazio a cui punta libro1

    Dopo tutto questo però, quando provo ad inserire un nuovo libro countlibri si blocca nel ciclo anche se in teoria il next di libro2 non è stato toccato e punta ancora a NULL. Spero di essermi spiegato in modo comprensibile A presto!

  2. #2
    Utente di HTML.it L'avatar di oregon
    Registrato dal
    Jul 2005
    residenza
    Roma
    Messaggi
    36,462
    Intanto un paio di considerazioni ...

    nLibri va azzerata all'inizio con

    nLibri=0;

    altrimenti il suo valore non è determinato.

    In secondo luogo, non è opportuno usare la parola new come parametro degli argomenti delle funzioni perché i compilatori C++, naturalmente, non lo "digeriscono". Usa nuovoLibro o nuovo

    Per ultimo, il problema mi sembra più a monte dato che non sono riuscito ad inserire neanche un libro senza avere un errore. Il problema è nell'uso del puntatore

    inizio

    e della prima malloc. A cosa ti serve allocare un primo elemento se non hai ancora fatto inserimenti? Quel puntatore dovrebbe essere semplicemente

    Libro *inizio = NULL;
    No MP tecnici (non rispondo nemmeno!), usa il forum.

  3. #3
    Hai perfettamente ragione, non capisco come sia possibile che non mi abbia mai creato problemi riguardo nLibri non inizializzato in effetti..

    L'idea di inizio allocato in quel modo era quella di avere un primo elemento immutabile. Se ne può fare a meno ?

    Compilando esattamente lo stesso codice il problema è che non mi da errori inserendo libri dal principio. E' proprio questo che mi confonde.. Provo a fare alcune modifiche seguendo i consigli che mi hai dato magari eliminando alcune funzioni come add2tail incorporandola in nuovoLibro. Intanto ti ringrazio Oregon, appena ho finito aggiorno questo post.

  4. #4
    Ho fatto alcune modifiche alla funzione nuovoLibro, a quanto pare però così mi da segmentation fault quando provo ad accedere al puntatore inizio al di fuori della funzione. è sbagliato come tipo di approccio? Ho qualche dubbio sul come far interagire le funzioni con la lista in modo diretto.. Utilizzando semplicemente il puntatore non sembra funzionare.

    codice:
    int main(int argc, char *argv)
    {
    	int select, nLibri=0, mod;
    	Libro *inizio = NULL;
    	
    	do{ //cicla fino a quando l'utente decide di terminare.
    		makeMenu(&select);
    		while (getchar() != '\n');
    		
    		switch(select){
    			case 0: //exit
    				break;
    			case 1: //alloca libro, lo accoda e aggiorna nLibri
    				if(nLibri < SIZE){
    					nuovoLibro(inizio, nLibri);
    					nLibri = countlibri(inizio);
    					printf("Libro inserito correttamente(%d/%d).\n", nLibri, SIZE);
    				}else
    					printf("Lo spazio sugli scaffali della biblioteca è esaurito.\n");
    				break;
    			case 2: //stampa lista
    				if(nLibri!=0)
    					stampLibri(inizio); 
    				else
    					printf("Non ci sono libri.\n");
    				break;
    			case 5: //rimuove un libro dalla lista, aggiorna nLibri
    				if(nLibri!=0){
    					remLibri(inizio, nLibri);
    					nLibri = countlibri(inizio);
    					printf("Libro rimosso con successo(%d/%d).\n", nLibri, SIZE);
    				}else
    					printf("Non ci sono libri.\n");
    				break;
    		}
    	}while(select != 0);
    	destLibri(inizio);
    return 0;
    }
    
    void makeMenu(int *select)
    {
    	printf("\t\t==M E N U==\n"
    		   "0- Esci.\n1- Inserisci Libro.\n2- Visualizza Libri.\n3- Ricerca libri(1+ matching).\n4- Ricerca libri(full matching).\n5- Rimuovi libro.\n>");
    	scanf("%d", select);
    }
    
    void nuovoLibro(Libro *inizio, int nLibri)
    {
    	Libro *p;
    	Libro *temp = inizio;
    	
    	p= (Libro *) malloc(sizeof(Libro));
    		
    	printf("Inserire l'ID del libro: ");
    	scanf("%d", &p->ID);
    	while (getchar() != '\n'); //per pulire il buffer dopo scanf
    	printf("Inserire l'Autore: ");
    	fgets(p->Autore, 255, stdin);
    	printf("Inserire il Titolo: ");
    	fgets(p->Titolo, 255, stdin);
    	printf("Inserire le 5 parole chiavi, separate da spazi: ");
    	fgets(p->Parole, 255, stdin);
    	
    	if(nLibri == 0){		   //se è l'inizio della lista faccio puntare inizio all'elemento creato
    		inizio = p;
    	}else{				       //altrimenti scorro fino alla fine della lista e lo aggiungo in coda
    		while(temp->next)	   
    			temp= temp->next;
    		temp->next = p;
    	}
    }

  5. #5
    Utente di HTML.it L'avatar di oregon
    Registrato dal
    Jul 2005
    residenza
    Roma
    Messaggi
    36,462
    Dato che devi modificare il valore del puntatore iniziale, lo devi passare, a sua volta, per puntatore. Quindi devi usare un doppio puntatore con tutto quello che ne consegue

    void nuovoLibro(Libro **inizio, int nLibri)
    No MP tecnici (non rispondo nemmeno!), usa il forum.

  6. #6
    Utente di HTML.it L'avatar di Scara95
    Registrato dal
    Jul 2009
    residenza
    Zimella (VR)
    Messaggi
    2,590
    Se vuoi questa è un'implementazione abbastanza completa...
    Ti basta sostituire la definizione di Info_t, compare e printList...
    Notare che puoi utilizzare diverse funzioni come funzione per comparare, perciò puoi usare più criteri per rimuovere, cercare o inserire.
    Utilizzando solo insert (sempre con la stessa funzione) avrai una lista ordinata secondo il criterio della funzione.
    Il main è solo codice di test.
    codice:
    #include <stdio.h>
    #include <stdlib.h>
     
    typedef int Info_t;
    typedef struct List_t {
        Info_t info;
        struct List_t *next;
    } List_t;
     
    typedef List_t *List;
    typedef int (*compare_f)(Info_t, Info_t);
     
    List cons(List *l, Info_t inf) {
        List tmp = (List)malloc(sizeof(List_t));
        tmp->info = inf;
        tmp->next = (*l);
        (*l) = tmp;
        return tmp;    
    }
     
    List insert(List *l, Info_t inf, compare_f f) {
        if((*l)==NULL)
            return cons(l, inf);
        List tmp;
        if(f(inf, (*l)->info) <= 0) {
            tmp = (List)malloc(sizeof(List_t));
            tmp->info = inf;
            tmp->next = (*l);
            (*l) = tmp;
            return tmp;
        }    
        tmp = (*l);
        while((tmp->next != NULL) && (f(inf, tmp->info) > 0)) tmp = tmp->next;
        List tmp1 = tmp->next;
        tmp->next = (List)malloc(sizeof(List_t));
        tmp->next->next = tmp1;
        tmp->next->info = inf;
        return (*l);    
    }
     
    List search(List l, Info_t inf, compare_f f) {
        while(l != NULL) {
            if(f(inf, l->info)==0) {
                return l;
            }
            l = l->next;
        }
        return NULL;
    }
     
    List rem(List *l, Info_t inf, compare_f f) {
        if((*l) == NULL)
            return NULL;
        List tmp;
        if(f((*l)->info, inf) == 0) {
            tmp = (*l);
            (*l) = (*l)->next;
            free(tmp);
            return (*l);
        }
        tmp = (*l);
        List tmp1;
        while((tmp->next != NULL) && (f(tmp->next->info, inf) != 0)) tmp = tmp->next;
        if(tmp->next != NULL) {
            tmp1 = tmp->next;
            tmp->next = tmp->next->next;
            free(tmp1);
        }
        return (*l);
    }
     
    void destroy(List l) {
        if(l == NULL)
            return;
        destroy(l->next);
        free(l);
    }
     
    void printList(List l) {
        while(l != NULL) {
            printf("%d ", l->info);
            l = l->next;
        }
        printf("\n");
    }
     
    int compare(Info_t a, Info_t b) {
        if(a<b) {
            return -1;
        } else if(a>b) {
            return 1;
        } else {
            return 0;
        }
    }
     
    int main() {
        List l = NULL;
        cons(&l, 4);
        cons(&l, 3);
        cons(&l, 5);
        cons(&l, 1);
        printList(l);
        destroy(l);
        l = NULL;
        insert(&l, 4, compare);
        insert(&l, 3, compare);
        insert(&l, 5, compare);
        insert(&l, 1, compare);
        printList(l);
        printList(search(l, 4, compare));
        printList(rem(&l, 3, compare));
        destroy(l);
        return 0;
    }
    "Quid enim est, quod contra vim sine vi fieri possit?" - Cicerone, Ad Familiares

  7. #7
    Dato che devi modificare il valore del puntatore iniziale, lo devi passare, a sua volta, per puntatore. Quindi devi usare un doppio puntatore con tutto quello che ne consegue

    void nuovoLibro(Libro **inizio, int nLibri)
    Io userei un reference a puntatore.
    codice:
    void nuovoLibro(Libro *&inizio, int nLibri)

  8. #8
    Utente di HTML.it L'avatar di Scara95
    Registrato dal
    Jul 2009
    residenza
    Zimella (VR)
    Messaggi
    2,590
    Originariamente inviato da lovesemiramide
    Io userei un reference a puntatore.
    codice:
    void nuovoLibro(Libro *&inizio, int nLibri)
    In C non esiste, devi usare un puntatore per questo.
    "Quid enim est, quod contra vim sine vi fieri possit?" - Cicerone, Ad Familiares

  9. #9
    Ho fatto delle modifiche, e sono (in parte) ritornato sui miei passi sul come creare la lista, per l'inserimento ora dovremmo esserci:
    codice:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define SIZE 10
    
    typedef struct Libro *lista;
    typedef struct Libro{
    	int ID;
    	char Autore[255];
    	char Titolo[255];
    	char Parole[255];
    	lista next;
    } Libro;
    
    Libro * nuovoLibro(void);						// Restituisce un puntatore ad un nuovo libro allocato.
    void makeMenu(int *select);						// Stampa il menu e acquisice la scelta.
    void cercapar(Libro *p, int nLibri, int mod);	// Cerca i libri in base alle parole chiave e modalità.
    void destLibri(Libro *p);						// Dealloca tutta la lista.
    void stampLibri(Libro *p);						// Stampa la lista completa.
    void remLibri(Libro **p, int nLibri);			// Rimuove il libro selezionato dall'elenco.
    void add2tail(Libro **p, Libro **new);			// Aggiunge un elemento in coda.
    int countlibri(Libro *p);						// Conta gli elementi della lista.
    
    int main(int argc, char *argv)
    {
    	int select, nLibri=0, mod;
    	Libro *inizio = NULL;
    	Libro *p;
    	
    	do{ //cicla fino a quando l'utente decide di terminare.
    		makeMenu(&select);
    		while (getchar() != '\n');
    		
    		switch(select){
    			case 0: //exit
    				break;
    			case 1: //alloca libro, lo accoda e aggiorna nLibri
    				if(nLibri<SIZE){
    					p = nuovoLibro();
    					add2tail(&inizio, &p);
    					nLibri = countlibri(inizio);
    					printf("Libro inserito correttamente(%d/%d).\n", nLibri, SIZE);
    				}else
    					printf("Lo spazio sugli scaffali della biblioteca è esaurito.\n");
    				break;
    			case 2: //stampa lista
    				if(nLibri!=0)
    					stampLibri(inizio);
    				else
    					printf("Non ci sono libri.\n");
    				break;
                            case 5: //rimuove un libro dalla lista, aggiorna nLibri
    				if(nLibri!=0){
    					remLibri(&inizio, nLibri);
    					nLibri = countlibri(inizio);
    					printf("Libro rimosso con successo(%d/%d).\n", nLibri, SIZE);
    				}else
    					printf("Non ci sono libri.\n");
    				break;
    		}
    	}while(select != 0);
    	destLibri(inizio);
    return 0;
    }
    
    Libro * nuovoLibro(void)
    {
    	Libro *p;
    	p= (Libro *) malloc(sizeof(Libro));
    	
    	printf("Inserire l'ID del libro: ");
    	scanf("%d", &p->ID);
    	while (getchar() != '\n'); //per pulire il buffer dopo scanf
    	printf("Inserire l'Autore: ");
    	fgets(p->Autore, 255, stdin);
    	printf("Inserire il Titolo: ");
    	fgets(p->Titolo, 255, stdin);
    	printf("Inserire le 5 parole chiavi, separate da spazi: ");
    	fgets(p->Parole, 255, stdin);
    	return p;
    }
    
    int countlibri(Libro *p)
    {
    	int i = 1;
    	while(p->next != NULL ){
    		i++;
    		p = p->next;
    	}
    	return i;
    }
    
    void add2tail(Libro **p, Libro **new)
    {	
    	Libro *inizio = *p;
    	if(*p){							//se inizio non è null scorro alla fine della lista
    		while((*p)->next) 
    			*p = (*p)->next;
    		(*p)->next = *new;
    		*p = inizio;				//per non perdere l'inizio della lista
    	}else							//altrimenti lo metto in testa
    		*p = *new;
    	
    }
    
    void destLibri(Libro *p)
    {
    	if(p == NULL) 
    		return;
    	Libro *temp;
    	temp = p->next;
    	free(p);
    	destLibri(temp);
    }
    Ma ora ho un dubbio:

    Riguardo la funzione destLibri (quella che si occupa della deallocazione della lista). In questo caso passo un puntatore singolo alla lista; Sto comunque liberando lo spazio della lista, oppure sto agendo su solamente su copie temportanee? (destLibri la chiamo in fondo, subito dopo il do-while).

  10. #10
    Utente di HTML.it L'avatar di Scara95
    Registrato dal
    Jul 2009
    residenza
    Zimella (VR)
    Messaggi
    2,590
    (Ammesso sia corretta la funzione) stai comunque liberando la memoria in quanto copi il puntatore (cioè l'indirizzo della memoria) e non l'elemento puntato.
    "Quid enim est, quod contra vim sine vi fieri possit?" - Cicerone, Ad Familiares

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2024 vBulletin Solutions, Inc. All rights reserved.