Linguaggio C: Cancellazione di un elemento da una lista.

  • Autore discussione Autore discussione Nines
  • Data d'inizio Data d'inizio
Pubblicità

Nines

Nuovo Utente
Messaggi
13
Reazioni
0
Punteggio
25
Salve a tutti. Mi sto da poco avvicinando al linguaggio C. Non ne necessito in maniera professionale, ma mi bastano solo alcune infarinature di base. Sto scrivendo un programma di gestione degli abbonamenti ad una palestra. Il programma in se stesso funziona (almeno nelle parti già scritte). Come unico problema, di cui non riesco a venire a capo, è che cercando di cancellare il primo elemento di una lista formata così:


struct nodo
{
char nome [30];
char cognome [30];
char abbonamento [11];
int giorno;
int mese;
int anno;
struct nodo *next;
};

il programma cancella solo la parte relativa al nome. Mantenendo in lista cognome, abbonamento, ecc... La funzione con cui cancello è questa:

void canc1(struct nodo **head)
{
struct nodo *q;
q=(*head);
(*head)=(*head)->next;
free(q);
}

E questa è invece quella in cui richiamo canc1

void abbonamenti(struct nodo *head, int g, int m, int a)
{
FILE *fp;
fp=fopen("scaduti", "a");
printf("\n\n Oggi scadono i seguenti abonamenti:\n ");
while(head!=NULL){
if((head->giorno==g)&&(head->mese==m)&&(head->anno==a))
{
printf("%s %s %s %d/%d/%d\n", head->nome, head->cognome, head->abbonamento, head->giorno, head->mese, head->anno);
fprintf(fp,"%s %s %s %d/%d/%d\n", head->nome, head->cognome, head->abbonamento, head->giorno, head->mese, head->anno);
canc1(&head);
head=head->next;
} else head=head->next;
}
fclose(fp);
printf("\n\n");
system("pause");
}

Per completezza, anche se non so se ce ne sia davvero motivo copio anche il programma per intero. Dove sbaglio? Per compilare utilizzo codeblock

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


struct nodo
{
char nome [30];
char cognome [30];
char abbonamento [11];
int giorno;
int mese;
int anno;
struct nodo *next;
};


struct nodo *head;


void canc1 (struct nodo **);
void aggiungiAlfile ();
void aggiungi (struct nodo **, char *, char *, char *, int, int, int);
struct nodo *Inlista();
void print (struct nodo *);
void cancella (struct nodo **, char *, char *, char *, int, int, int);
void filescaduti (struct nodo *);
void abbonamenti (struct nodo*, int, int, int);


main()
{
struct nodo *head;
head=(struct nodo*)malloc(sizeof(struct nodo));
int g,m,a,x;


aggiungiAlfile();
head=Inlista();
printf("Inserisci il giorno di oggi (1/1/1): ");
scanf("%d/%d/%d", &g, &m, &a);
canc1(&head);
abbonamenti(head, g, m, a);
filescaduti (head);


while(x!=0)
{
system("cls");
printf("\n\n\n\t\t\t********************");
printf("\n\t\t\t**MENU' PRINCIPALE**");
printf("\n\t\t\t********************\n\n");
printf(" Inserisci:\n 1. Per visualizzare tutti gli abbonati.\n 2. Per visualizzare gli introiti di questo mese.\n 3. Per visualizzare il menu' gestione abonamenti scaduti.\n 0. Per uscire.\n\n\t");
scanf("%d", &x);
switch(x)
{
case 1: system("cls");
print(head);
printf("\n\n");
system("pause");
break;
case 2: system("cls");




printf("\n\n");
system("pause");
break;
case 3: system("cls");


printf("\n\n");
system("pause");
break;
case 0: system("cls");
printf("\n\n\n Grazie per aver utilizzato il nostro programma, arrivederci.\n\n\n");
printf("\n\n");
system("pause");
break;
default: printf(" Il programma non ha un riscontro dei casi inseriti, si prega di reinserire il\n dato di comando\n");
printf("\n\n");
system("pause");
break;
}
}




}


void canc1(struct nodo **head)
{
struct nodo *q;
q=(*head);
(*head)=(*head)->next;
free(q);
}


void aggiungiAlfile()
{
FILE *fp;
char a, b, nom[30], cog[30], abb[11];
int x, i=0, gio, mes, ann;


fp=fopen("abbonati","a");
printf("\n\n Vuoi aggiungere uno o piu' abbonati non presenti nell'archivio? (si no): ");
scanf("%c", &a); fflush(stdin);
system("cls");
if(a=='s')
{
printf("\n\n Quanti nuovi abbonati vuoi catalogare? ");
scanf("%d", &x); fflush(stdin);
while (i<x)
{
printf("\n Inserisci il nome del nuovo abbonato: ");
scanf("%s", nom); fflush(stdin);
printf("\n Inserisci il cognome del nuovo abbonato: ");
scanf("%s", cog); fflush(stdin);
printf("\n Inserisci il tipo di abonamento scelto: ");
scanf("%s", abb); fflush(stdin);
printf("\n Inserisci da data di scadenza dell'abonamento nella forma: gg/mm/aa (1/1/12):\n ");
scanf("%d/%d/%d", &gio,&mes,&ann); fflush(stdin);
system("cls");
printf("\n\n Hai inserito: %s, %s, %s, %d/%d/%d.\n\n E' tutto corretto? (si no): ", nom, cog, abb, gio, mes, ann);
scanf("%c", &b);
switch(b)
{
case 's': fprintf(fp,"%s %s %s %d/%d/%d\n", nom, cog, abb, gio, mes, ann);
break;
case 'n': i--;
break;
default: printf("\n\nNon hai scritto ne sì ne no, riprova!\n\n\n");
system ("pause");
break;
}


i++;
fflush(stdin);
}
}
fclose(fp);
return;
}


void aggiungi(struct nodo **head, char *nom, char *cog, char *abb, int gio, int mes, int ann)
{
struct nodo *p;
struct nodo *temp;


p=(struct nodo*)malloc(sizeof(struct nodo));
strcpy(p->nome, nom);
strcpy(p->cognome, cog);
strcpy(p->abbonamento, abb);
p->giorno=gio;
p->mese=mes;
p->anno=ann;
p->next=NULL;
temp=(*head);
while (temp->next != NULL)
temp = temp->next;
temp->next = p;
}


struct nodo *Inlista()
{
FILE *fp;
char nom[30], cog[30], abb[11];
int gio, mes, ann;
struct nodo *head;


head=(struct nodo*)malloc(sizeof(struct nodo));
fp=fopen("abbonati","r");
strcpy(head->nome, "NOME");
strcpy(head->cognome, "COGNOME");
strcpy(head->abbonamento, "ABBONAMENTO");
head->giorno=0;
head->mese=0;
head->anno=0;
head->next=NULL;
while(!feof(fp))
{
fscanf(fp,"%s %s %s %d/%d/%d\n", nom, cog, abb, &gio, &mes, &ann);
aggiungi (&head, nom, cog, abb, gio, mes, ann);
}
fclose(fp);
return head;
}


void print(struct nodo *head)
{
system("cls");
printf("\n\n");
while(head!=NULL)
{
printf(" %s %s %s %d/%d/%d\n", head->nome, head->cognome, head->abbonamento, head->giorno, head->mese, head->anno);
head=head->next;
}
}


void filescaduti (struct nodo *head)
{
FILE *fp;
fp=fopen("*****","w");
while(head!=NULL)
{
printf("%s %s\n", head->nome, head->cognome);
fprintf(fp,"%s %s %s %d/%d/%d\n", head->nome, head->cognome, head->abbonamento, head->giorno, head->mese, head->anno);
head=head->next;
}
fclose(fp);


}




void abbonamenti(struct nodo *head, int g, int m, int a)
{
FILE *fp;
fp=fopen("scaduti", "a");
printf("\n\n Oggi scadono i seguenti abonamenti:\n ");
while(head!=NULL){
if((head->giorno==g)&&(head->mese==m)&&(head->anno==a))
{
printf("%s %s %s %d/%d/%d\n", head->nome, head->cognome, head->abbonamento, head->giorno, head->mese, head->anno);
fprintf(fp,"%s %s %s %d/%d/%d\n", head->nome, head->cognome, head->abbonamento, head->giorno, head->mese, head->anno);
canc1(&head);
head=head->next;
} else head=head->next;
}
fclose(fp);
printf("\n\n");
system("pause");
}
 
Ciao, così, ad una prima rapida guardata, mi salta subito all'occhio quel free(q) in canc1
quella istruzione dealloca un puntatore e non l'area a cui il puntatore del puntatore punta

vedi se è così seno' lo guardo meglio
 
Ciao, così, ad una prima rapida guardata, mi salta subito all'occhio quel free(q) in canc1
quella istruzione dealloca un puntatore e non l'area a cui il puntatore del puntatore punta

vedi se è così seno' lo guardo meglio


Scusa, ma il mio livello è davvero basso. Credo di aver capito cosa intendi, ma non ho assolutamente idea di cosa usare al posto di free(q) :\
 
Scusa, ma il mio livello è davvero basso. Credo di aver capito cosa intendi, ma non ho assolutamente idea di cosa usare al posto di free(q) :\
ho appena fatto un debug, e la deallocazione è corretta, gli unici errori erano nel nome del file nella funzione filescaduti (devi mettere un nome corretto),
e la x del ciclo while che non era inizializzata, seno funziona.
 
ho appena fatto un debug, e la deallocazione è corretta, gli unici errori erano nel nome del file nella funzione filescaduti (devi mettere un nome corretto),
e la x del ciclo while che non era inizializzata, seno funziona.

In che senso mettere un nome corretto?
A me non funziona. Almeno: Ti fa inserire correttamente i nuovi abbonati, li salva correttamente nel file, trova quelli che scadono, e funziona correttamente, finché quando nel menu' schiaccio 1 (stampa lista) invece di cancellare l'abbonamento scaduto, cancella solo il nome.

Se per esempio, prima di aprire il programma il file è composto da:
tizio1 caio1 mensile 1/1/1
tizio2 caio2 mensile 2/2/2
tizio3 caio3 mensile 3/3/3

dopo che faccio cancellare il primo, la lista diventa:
caio1 mensile 1/1/1
tizio2 caio2 mensile 2/2/2
tizio3 caio3 mensile 3/3/3

invece di essere:
tizio2 caio2 mensile 2/2/2
tizio3 caio3 mensile 3/3/3

a te non da questo problema?

A quale while ti riferisci?
 
In che senso mettere un nome corretto?
A me non funziona. A quale while ti riferisci?

fp=fopen("*****","w"); // devi mettere un nome di file valido, non ****

while(x!=0) // nel main, utilizzi la x senza prima inizializzarla

ho fatto un copia e incolla di quello che hai postato; ho fatto il debug,
funziona tutto una volta corrette quelle due cose. la lista veniva visualizzata correttamente.
L'ambiente utilizzato è il visual studio 2008.
Non so cos'altro dirti
 
Ciao a tutti... scusa Trepiedi ma che programma usi per fare il debug? E per Nines usa un do while nel main al posto di quel while, cosi puoi anche non inizializzare la x perché tanto ci assegnerai un valore dopo..
Comunque quella funzione per la cancellazione funziona (IMHO), a occhio mi sembrava andasse e poi l'ho anche provata, quindi tranquillo..
 
Ultima modifica:
Ciao a tutti... scusa Trepiedi ma che programma usi per fare il debug? E per Nines usa un do while nel main al posto di quel while, cosi puoi anche non inizializzare la x perché tanto ci assegnerai un valore dopo..
Comunque quella funzione per la cancellazione funziona (IMHO), a occhio mi sembrava andasse e poi l'ho anche provata, quindi tranquillo..
Si si funziona, anche se non deallocasse, al limite avrebbe un'area sprecata ma head indirizza cmq bene il nodo successivo; siccome aveva detto che non cancellava
ho guardato velocemente solo quella funzione.

Come ambiente uso il Visual Studio, il debugger lì è roba da antologia.
 
Ma quindi voi l'avete provato inserendo dati, avete capito il mio problema qual'è? perché a me continua a dare questo strano errore :\

Ah, il nome c'era, ma nella frustrazione avevo usato una parolaccia che poi il sito ha sostituito con ***** :D
 
Il mio C e' un po' arrugginito pero' mi mi pare che la tua funzione di cancellazione elimini solo la prima cella di memoria indicata dal puntatore quindi sposta l'header sul secondo campo, il cognome. Prova a ripetere l'operazione di "free" per ogni campo con un ciclo for.
 
Il mio C e' un po' arrugginito pero' mi mi pare che la tua funzione di cancellazione elimini solo la prima cella di memoria indicata dal puntatore quindi sposta l'header sul secondo campo, il cognome. Prova a ripetere l'operazione di "free" per ogni campo con un ciclo for.

Come posso farlo? Nel mio immaginario head dovrebbe puntare a tutto lo struct... Non saprei proprio come impostare il ciclo for...
Anche se a dir la verità il mio problema è variato leggermente... dopo aver inizializzato x adesso il programma cancella il nome e scrive un carattere casuale al posto del cognome O.o

Adesso sono davvero scoraggiato...
 
Ok ho capito il problema: non hai salvato da nessuna parte l'elemento precedente della tua lista. Quando cancelli un elemento il contenuto di (*head)->next non deve andare in (*head) bensi' nel campo next della struct precedente nella lista (visto che l'elemento puntato da head e' quello che vuoi cancellare).

Per evitare di trattare casi particolari ti suggerisco di inserire un campo *prev nella tua struct che punti all'elemento precedente in lista, quindi vai poi semplicemente a mettere nel campo next della struct puntata da
(*head)->prev il contenuto di (*head)->next.

Cosi' dovrebbe funzionare.
 
Ok ho capito il problema: non hai salvato da nessuna parte l'elemento precedente della tua lista. Quando cancelli un elemento il contenuto di (*head)->next non deve andare in (*head) bensi' nel campo next della struct precedente nella lista (visto che l'elemento puntato da head e' quello che vuoi cancellare).

Per evitare di trattare casi particolari ti suggerisco di inserire un campo *prev nella tua struct che punti all'elemento precedente in lista, quindi vai poi semplicemente a mettere nel campo next della struct puntata da
(*head)->prev il contenuto di (*head)->next.

Cosi' dovrebbe funzionare.


Premettendo che molto probabilmente non ho capito bene cosa dover fare, ho apportato a canc1 questa modifica:

void canc1(struct nodo **head)
{
struct nodo *q;
q=(*head);
(*head)=(*head)->next;
(*head)->prev=(*head)->next;
free(q);
}

Però così facendo non cancella nulla...
 
Premettendo che molto probabilmente non ho capito bene cosa dover fare, ho apportato a canc1 questa modifica:

void canc1(struct nodo **head)
{
struct nodo *q;
q=(*head);
(*head)=(*head)->next;
(*head)->prev=(*head)->next;
free(q);
}

Però così facendo non cancella nulla...


Perche' devi cambiare anche la struct:
struct nodo
{
char nome [30];
char cognome [30];
char abbonamento [11];
int giorno;
int mese;
int anno;
struct nodo *next;
struct nodo *prev;
};

A questo punto la funzione di cancellazione diventa:

void canc1(struct nodo **head)
{
struct nodo *q;
q=(*head);
(*head)=(*head)->next;
((*head)->prev)->next=(*head)->next;
free(q);
}

Non dimenticarti di modificare anche la funzione di inserimento per introdurre un puntatore al nodo precedente.
 
Perche' devi cambiare anche la struct:
struct nodo
{
char nome [30];
char cognome [30];
char abbonamento [11];
int giorno;
int mese;
int anno;
struct nodo *next;
struct nodo *prev;
};

A questo punto la funzione di cancellazione diventa:

void canc1(struct nodo **head)
{
struct nodo *q;
q=(*head);
(*head)=(*head)->next;
((*head)->prev)->next=(*head)->next;
free(q);
}

Non dimenticarti di modificare anche la funzione di inserimento per introdurre un puntatore al nodo precedente.


Niente, apportando queste modifiche crasha quando richiamo la funzione abbonamenti :\
 
Pubblicità
Pubblicità
Indietro
Top