[Hilfe] löschfunktion in datenverwaltungsliste implementieren



  • und das nächste problem, nicht das langeweile aufkommt 😃

    ich versuche nun schon seit stunden eine löschfunktion zu implementieren.
    das erste element zu löschen stellt kein großes problem dar.
    bei einem element in der mitte scheint er jedoch irgendwas dummes zu machen.
    ich bekomme eine fehlermeldung und das programm stürzt ab, sobald ich versuche die liste nochmals anzeigen zu lassen, mit dem gelöschten element.

    die löschfunktion ist ganz unten vor der main zu finden, ich hoffe so ist der stil etwas besser 😉

    mein code sieht mitlerweile so aus:

    #include "neu.h"
    
    int menu_ausgabe(void)
    { 
    	int auswahl;
    	do
    	{
    	printf("\n menue\n --------------\n 1...Eingabe\n 2...Ausgabe\n 3...Suchen\n 4...Loeschen\n 5...in datei speichern\n 6...aus datei lesen\n");
    	scanf("%d", &auswahl);
    	}
    	while ((auswahl>6) || (auswahl<1));
    
    	return auswahl;
    
    }
    
    typedef struct person
    {	char name[20];
    	int plz;
    	char wohnort[20];
    	char strasse[20];
    	int hausnummer;
    	struct person *next;
    }person; 
    
    person* Eingabe(person *head)//funktion zur eingabe der daten in die liste
    {	person  *neu;
    	char j='j';
    
    	while(j=='j')
    	{
    		neu= (person*)malloc(sizeof(person));
    
    		printf("name:\n");fflush(stdin);
    		gets(neu->name);
    		/*printf("geben sie die Plz an:\n");fflush(stdin);
    		scanf("%i",&neu->plz);
    		puts("geben sie den Wohnort an:\n");fflush(stdin);
    		gets(neu->wohnort);
    		puts("geben sie die strasse an:\n");fflush(stdin);
    		gets(neu->strasse);
    		puts("geben sie die Hausnummer an:\n");fflush(stdin);
    		scanf("%i",&neu->hausnummer);*/
    
    		neu->next = head;
    		head = neu;
    		fflush(stdin);
    		printf("noch einen Wert einlesen? j/n:\n");
    		scanf("%c",&j);
    
    	}
    	return head;
    }
    
    void ausgabe(person*anfang)
    
    {if(anfang == 0)
    printf("keine Datensaetze vorhanden");
    else
    {
    printf("name:  %s\n",anfang->name);
    /*printf("Plz:  %i\n",anfang->plz);
    printf("Ort:  %s\n",anfang->wohnort);
    printf("Strasse:  %s\n",anfang->strasse);
    printf("Hausnummer:  %i\n\n\n",anfang->hausnummer);*/
    
    if(anfang->next !=0)
    	ausgabe(anfang->next);
    }
    }
    
    void suche(char*suchname,person*anfang)//liefert direkt die gesamtdaten der gesuchten person. übernimmt zu suchende person, und struct in der gesucht werden soll
    {	
    	if(anfang==0)
    	printf("noch keine Datensatze vorhanden");
    	else
    	{
    	if(strcmp(anfang->name,suchname)==0)
    	{
    		printf("\ngefundener name: %s\n",anfang->name);
    		/*printf("Plz:  %i\n",anfang->plz);
    		printf("Ort:  %s\n",anfang->wohnort);
    		printf("Strasse:  %s\n",anfang->strasse);
    		printf("Hausnummer:  %i\n\n\n",anfang->hausnummer);*/
    	}
    	if(anfang->next!=0)
    	suche(suchname, anfang->next);
    	else
    		printf("ende der liste erreicht");
    	}
    
    }
    
    person* Loeschen(person*aktuelles_element,char*suchname,person*head_alt)
    {
    person*head;
    person*puffer;
    	if(aktuelles_element==0)
    	{
    		printf("noch keine datensätze vorhanden");
    		return head_alt;
    	}
    	if(aktuelles_element->next==0)
    	{
    		printf("keinen solchen namen gefunden");
    
    		head=head_alt;
    		return head;
    	}
    
    else
    	{
    		if(strcmp(suchname,aktuelles_element->name)==0)
    		{   
    			if(aktuelles_element==head_alt)//ist das zu löschende element das erste??
    			{
    			head = aktuelles_element->next;
    			free(aktuelles_element); 
    			printf("loeschen erfolgreich");
    			return head;
    
    			}
    			else //das zu löschende element ist nicht das erste-> der head pointer muss der alte bleiben
    			{
    				head=aktuelles_element;
    				puffer=aktuelles_element->next;
    				free(aktuelles_element);
    				head=puffer;
    				printf("loeschen erfolgreich");
    				return head_alt;
    
    			}
    		}
    
    else
    	Loeschen(aktuelles_element->next,suchname,head_alt);
    
    	}
    }
    
    main()
    {	
    	person *zgr_person=0;
    	person *head=0;
    	int auswahl;
    	char sucher[20];
    	char*suchname;
    
    	while(1)
    	{
    	auswahl = menu_ausgabe();//ausgabe des menus, übdernahme der auswahl
    
    	if(auswahl == 1)
    	zgr_person = Eingabe(head);// Eingabe der Datensätze
    
    	head = zgr_person; //Wichtig!! übergibt den head beim erneuten aufruf an die eingabe(), sodass diese den alten anfang wieder findet
    
    	if(auswahl == 2)
    	ausgabe(zgr_person);//ausgabe der Datensätze
    
    	if(auswahl == 3)
    	{
    	printf("nach wem soll gesucht werden?:\n");
    
    	fflush(stdin);
    	gets(sucher);
    	suchname=sucher;
    
    	suche(suchname,zgr_person);
    
    	}
    
    	if (auswahl==4)
    	{
    		printf("wer soll geloescht werden?\n");
    		fflush(stdin);
    		gets(sucher);
    		suchname=sucher;
    
    		zgr_person=Loeschen(head,suchname,head);
    
    		head=zgr_person;
    	}
    
    }
    system("PAUSE");
    return 0;
    }
    

    und die header:

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

    als erklärung:
    ich habe einen pointer als puffer der ursprüngliche head bleibt immer gleich, beim aktuellen element wandere ich solange, bis ich den gesuchten namen gefunden habe.
    dann verbinde ich den head pointer mit dem aktuellen element danach verbinde ich den puffer pointer mit dem next des aktuellen elementes. nun lösche ich das aktuelle element und verbinde den head pointer mit dem puffer, so dass die entstandene lücke geschlossen wird.

    zurückgegeben wird der ursprüngliche anfang, denn an dem ändert sich ja nichts.

    ich bin im prinzip schon stolz das von der überlegung soweit gebracht zu haben :p nur leider geht es einfach nicht und ich weiss nicht mehr weiter 😞

    ist sicher nur ein kleiner denkfehler

    €:der fehler liegt wohl darin, dass alles was ich in der funktion verändere gar keine auswirkung auf die main hat, da ich einfach nur den alten pointer zurückgebe, richtig?
    wie ich das beheben soll weiss ich allerdings noch nicht

    lg



  • FedX schrieb:

    die löschfunktion ist ganz unten vor der main zu finden, ich hoffe so ist der stil etwas besser 😉

    So ists viel besser. Du kannst sogar noch den restlichen Inhalt der .h-Datei in die .c schreiben und die .h ganz weglassen. Ist für Dein Programm (wo Du ja nicht mit mehreren .c Dateien arbeitest) nicht notwendig. Eigene .h-Dateien brauchst Du grob gesagt nur, wenn Du mit mehreren .c-Dateien arbeitest und untereinander auf Funktionen/Variablen zugreifen möchtest.

    Mit welcher Entwicklungsumgebung arbeitest Du? Versuche mit dem Debugger zu arbeiten, dann findest Du selbst die Fehler.

    Kurze Info zum Codeausschnitt als Kommentar:

    FedX schrieb:

    ...
    			else //das zu löschende element ist nicht das erste-> der head pointer muss der alte bleiben
    			{
    				head=aktuelles_element;          // Unnütze Aktion, Du benutzt head nirgends
    				puffer=aktuelles_element->next;  // puffer enthält nun den nächsten gültigen Eintrag nach dem 
                                       // zu löschenden Eintrag. Achtung! ist null beim letzten Eintrag.
    				free(aktuelles_element);        // jetzt löscht Du den aktuellen Eintrag, dummerweise steht aber im vorherigen Eintrag
                                             // noch unter next der aktuelle, jetzt gelöschte Eintrag und wird deshalb beim auflisten einen Speicherfehler verursachen.
    				head=puffer;                    // Unnütze Aktion, Du benutzt head nirgends
    				printf("loeschen erfolgreich");
    				return head_alt;
    				
    			}
    ...
    

    Fazit: Du löscht den gefundenen Eintrag, der vorherige zeigt nach dem Löschen aber dummerweise noch immer auf den dann gelöschten. Ich hoffe Du verstehst das Problem.



  • also unabhängig von dem code ist eine verkettete liste für sowas total ungeeignet. oder verlierst deine kunden im sekundentakt 😕

    @edit: damit wären wir wieder bei schlechter ausbildung. wie man sieht sind es nicht nur schlechte schüler sondern auch schlechte lehrer. was denkt ihr was jmd. macht der gelernt hat personen als verkettete liste zu speichern? natürlich nimmt er wieder ne verkettete liste denn das hat er ja so gelernt...



  • die aufgabe war eine dynamische liste zu nehmen.
    evtl liege ich damit mit einer linearen liste auch voll daneben, aber die erschien mir zunächst am einfachsten, als es nur um eingabe und ausgabe der struct ging.
    sollte man sowas lieber über bäume o.ä. machen?, oder doch ganz anders?

    zurück zum problem, ich verstehe was du meinst, aber ich dachte das hätte ich durch den head quasi als 2. puffer gelöst, dass das mist war ist mir jetzt klar geworden.

    aber wie komme ich nun an den vorherigen wert?, in einer doppelt verketteten liste wäre das wohl ziemlich leicht, in dieser einfachen muss ich noch ein argument übernehmen, das immer auf das element vor dem ausgewählten zeigt oder?

    lg



  • Die einfachste Möglichkeit ist sicher einen zusätzlichen Parameter, der auf den letzten Eintrag zeigt (natürlich null beim ersten Eintrag).

    Ich persönlich würde das ganze aber nicht über eine rekursive Funktion machen, sondern iterativ. Dann wird alles viel einfacher. Ich weiß nicht ob die Aufgabenstellung rekursiv war.

    Sonst mach Dir mal Gedanken, wie man das einfach durch eine for/while Schleife ohne Rekursion machen könnte.



  • habs rekursiv hinbekommen 🙂 🙂 🙂

    klasse danke.

    das mit den normalen schleifen versuch ich später noch irgendwann, ich war jetzt mit dem rekursiven schon so weit.

    jetzt versuche ich erstmal das ganze extern in n txt file zu speichern und aus nem txt file zu lesen.

    lg



  • FedX schrieb:

    fflush(stdin);
    

    Nicht standardkonform.

    FedX schrieb:

    while(1)
    	{
    	auswahl = menu_ausgabe();//ausgabe des menus, übdernahme der auswahl
    	if(auswahl == 1)
    	if(auswahl == 2)
    	if(auswahl == 3)
    	if(auswahl == 4)
            ...
    

    Kennst du switch/case?

    FedX schrieb:

    zgr_person=Loeschen(head,suchname,head);
    

    Sieht nicht gelungen aus, ist es auch.



  • naja mir geht es nicht um einen tollen programmierstil sondern nur darum, das grundgerüst einigermaßen zu verstehen und meine klausur zu bestehen.
    dass meine lösungen sicherlich nicht den elegantesten weg darstellen ist mir klar, immerhin konnte ich bis vor ein paar wochen noch gar nichts programmieren.

    switch case sagt mir nichts.

    mal eine andere frage, wenn ich ein file öffne und etwas reinschreibe, zb:

    FILE*file;
    
    if(file=(fopen("test.txt","w")))
    { fprintf(file,"test");
    }
    else
    printf("fehler beim öffnen der datei");
    

    so wird ja ein file mit dem namen test.txt abgelegt.
    aber wie kann ich den dateinamen dynamisch halten, sprich so, dass ich einen string eingebe und dieser dann der filename wird?

    ein versuch war:

    char filename[20];

    file=(fopen("%s.txt",filename,"w");

    das war meine erste idee, aber natürlich passt das der funktion nicht, da sie nun zu viele argumente enthält und nicht rafft was ich eigentlich bezwecken will.

    lg



  • FedX schrieb:

    mal eine andere frage, wenn ich ein file öffne und etwas reinschreibe, zb:

    wie heißt dein prof?



  • wieso willste das wissen?
    glaube nicht, dass das meine frage beantwortet

    lg



  • FedX schrieb:

    aber wie kann ich den dateinamen dynamisch halten, sprich so, dass ich einen string eingebe und dieser dann der filename wird?

    ein versuch war:

    char filename[20];

    file=(fopen("%s.txt",filename,"w");

    Mach statt der letzten Zeile

    dynamische_eingabe = "foo";
    sprintf(filename, "%s.txt", dynamische_eingabe);
    file = fopen(filename, "w");
    

    🙂



  • ordnet dieses sprintf filename den dynamischen namen zu?

    erwartet fopen keine argumente in gänsefüßchen?

    könnte ich dann auch einen pointer auf dynamischen filename setzen und in fopen öffnen?
    fopen(pointerfilename,"w") ich denke mal nicht, sonst hättest du es wohl kaum mit diesem sprintf gemacht richtig? ^^

    was ist dynamische eingabe bei dir ?
    ein weiterer char?
    irgendwie ergibt das für mich keinen sinn, sollte es ein weiterer char sein wieso reicht dann nich einfach filename?

    €: gutgut funzt, aber dieses sprintf ist mir nicht ganz klar was macht das denn genua?
    ich habe jetzt 2 char dyn_filename[20],filename[20];
    dyn_filename lese ich mit gets ein, dann mache ich
    sprintf(filename,".txt",dyn_filename);
    fopen(filename,"w");

    hänge ich mit dem sprintf einfach nur .txt an filename, sodass ich ein passendes formar in fopen stehen habe ??

    lg



  • FedX schrieb:

    erwartet fopen keine argumente in gänsefüßchen?

    Nö, es erwartet zwei Strings, d.h. zwei Zeiger auf Zeichen. Als String wird das Zeichen, auf das ein Zeiger zeigt, und alle folgenden aufgefasst, bis zur ersten Null.

    FedX schrieb:

    könnte ich dann auch einen pointer auf dynamischen filename setzen und in fopen öffnen?

    So?

    dynamische_eingabe = "foo";
    file = fopen(dynamische_eingabe, "w");
    

    Ja, aber dann heisst die Datei nur foo .

    FedX schrieb:

    was ist dynamische eingabe bei dir ?
    ein weiterer char?
    irgendwie ergibt das für mich keinen sinn, sollte es ein weiterer char sein wieso reicht dann nich einfach filename?

    Man könnte das Anhängen des ".txt" auch gleich im Speicherbereich des Eingabestrings machen, wenn dort genug Platz ist. Ich hab einen zweiten String angelegt, weil ich nicht sicher bin, ob sprintf() verwirrt wird, wenn man in einem Aufruf auf den gleichen String lesend und schreibend zugreift, und bin gerade zu faul, um das nachzuschlagen.

    FedX schrieb:

    hänge ich mit dem sprintf einfach nur .txt an filename, sodass ich ein passendes formar [ich lese: einen passenden Dateinamen] in fopen stehen habe ??

    Ja, aber das ganze in einem anderen String.

    Bitte etwas deutlicher schreiben, das Gewirr ist nicht ganz einfach zu verstehen.
    🙂



  • gut danke,
    als nächstes kommt noch eine lesefunktion, dann sollte das programm komplett sein.
    allerdings habe ich im moment keine lust mehr weiter zu machen.



  • wenn ich das ganze laden möchte, wäre es wahrscheinlich sinnvoller, das ganze mit fwrite zu speichern oder?

    aber was muss ich da als länge angeben?

    mein ansatz sieht so aus:

    void speichern(FILE*file,person*person_anfang)
    {
    	if((person_anfang->next)!=0)
    	{
    	fwrite(person_anfang,sizeof(person),1,file);
    	speichern(file,person_anfang->next);
    	}
    	else
    	printf("ende erreicht\n");
    }
    

    was muss ich bei der länge reinschreiben? habe jetzt einfach mal 1 gemacht, ist das dann 1 byte? woher weiss ich wie viel byte ich für mein struct brauche?

    v.a. wie öffne ich das ganze mit fread wieder und zwar so, dass mir der anfangspointer auf die gelesene liste zurück gegeben wird??

    lg



  • Der Ansatz ist erstmal richtig, aber rekursiv, das führt selbst bei einer geringen Stackbelastung wie hier bei größeren Datensatzanzahlen zu Stackproblemen.

    sizeof(person)
    

    liefert dir die Größe des Typs "person" in Bytes, ein Blick in dein Lehrbuch wäre hilfreich.
    Nach dem fwrite-Speichern liegen in der Datei auch dynamische Werte, d.h. die Zeiger, d.h. diese musst du beim Lesen wieder korrekt setzen, da sie höchstwahrscheinlich beim nächsten Programmaufruf ungültig sind.



  • Wutz schrieb:

    Der Ansatz ist erstmal richtig, aber rekursiv, das führt selbst bei einer geringen Stackbelastung wie hier bei größeren Datensatzanzahlen zu Stackproblemen.

    sizeof(person)
    

    liefert dir die Größe des Typs "person" in Bytes, ein Blick in dein Lehrbuch wäre hilfreich.
    Nach dem fwrite-Speichern liegen in der Datei auch dynamische Werte, d.h. die Zeiger, d.h. diese musst du beim Lesen wieder korrekt setzen, da sie höchstwahrscheinlich beim nächsten Programmaufruf ungültig sind.

    🙄


Anmelden zum Antworten