[Hilfe] löschfunktion in datenverwaltungsliste implementieren
-
Hi,
also ich habe ein programm geschrieben, welches mit hilfe einer linearen liste die daten von personen aufnimmt und ausgeben kann.
nun soll ich noch eine suche implementieren und da verzweifle ich bald.
zunächst habe ich versucht den gesuchten namen mit gets zu übernehmen und einen pointer darauf zeigen zu lassen und den mit dem vorhandenen zu datensatz zu vergleichen. ungefähr so:char sucher[20];
char*suchname;nach wem wird gesucht?
gets (sucher);
suchname=sucherwenn(suchname==anfang->name) aber es geht einfach nicht. hier mal mein aktueller coder:
c file:
#include "neu.h" int menu_ausgabe(); person * Eingabe(); main() { person *zgr_person=0; person *head=0; int auswahl; person * 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"); suchname = (person*)malloc(sizeof(person)); fflush(stdin); gets(suchname->name); printf("uebernommene suche:%s\n", suchname->name); suche(suchname,zgr_person); } } system("PAUSE"); return 0; }
header:
#include <stdio.h> #include <stdlib.h> #include <string.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...Beenden\n m...menue\n"); scanf("%d", &auswahl); } while ((auswahl>5) || (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(person*suchname,person*anfang)//liefert direkt die gesamtdaten der gesuchten person. übernimmt zu suchende person, und struct in der gesucht werden soll { printf("start der funktion"); if((anfang->name)==((suchname->name))) { printf("gefundener name: %s\n",anfang->name); } if(anfang->next!=0) suche(suchname, anfang->next); }
wär super wenn sichs mal jemand anschauen könnte.
nächste sache wäre dann noch einen schutz einzubauen, falls der gesuchte name nicht existiert.
die suchfunktion ist am ende der header der funktionsaufruf am ende des c files
ein paar sachen habe ich auskommentiert um nur das wesentliche anzuzeigen, dieses printf in der suchfunktion sollte mir nur zeigen das und wie oft die funktion startetlg
-
Wenn Du Strings vergleichen möchtest darfst Du nicht
str1 == str2
verwenden. Mit dem Codeif((anfang->name)==((suchname->name)))
vergleichst Du die beiden Pointer auf das jeweils erste Zeichen miteinander, welche IMMER verschieden sind, außer Du vergleichst zweimal dieselbe Struktur.
Um Stringvergleiche zu machen schau Dir mal diese Funktion an:
strcmp(...)
if ( strcmp( anfang->name, suchname->name ) == 0 )
PS: Du solltest Dir klar werden, was in eine .h und was in eine .c-Datei soll. Dein jetziger Aufbau ist grauenvoll.
Grüße,
Chriss
-
super danke.
ich bin absoluter anfänger und hab recht wenig ahnung davon. problem ist, dass ich nun durch beide semester in info durchgefallen bin und ne nachklausur schreiben darf(ich studiere e technik).
ich bin mir nicht sicher, aber ich glaube wir haben in den vorlesungen funktionen immer in der header geschrieben, aber ich kann mich auch täuschen, was sehr wahrscheinlich ist, denn wüsste ich genau was wir gemacht haben wäre ich kaum zwei mal durchgefallen
es wär super, wenn du mich aufklären könntest, was wo hin kommt.
was ich auch nicht verstehe, warum sind die ersten zeichen von pointern auf einen char immer unterschiedlich ?
wenn ich zweimal einen namen habe, der gleich ist und darauf jeweils einen pointer zeige, dann haben doch beide pointer den wert des namens oder?
mit strcmp() kann man sicher auch irgendwie eine sortierung vornehmen oder?
ich habe meine suchfunktion nun folgendermaßen geändert:
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"); if(strcmp(anfang->name,suchname)==0) { printf("gefundener name: %s\n",anfang->name); } if(anfang->next!=0) suche(suchname, anfang->next); else printf("keinen solchen eintrag gefunden"); }
ich lese einen char sucher[20] ein und lasse einen pointer char*suchname darauf zeigen.
an sich funktioniert es nun, allerdings bekomme ich einen fehlerberichtm wenn ich nach einem namen suche, bevor ich einen datensatz eingebe.
nur warum was ist falsch? €:habs selbst rausgefundenlg
-
FedX schrieb:
ich bin mir nicht sicher, aber ich glaube wir haben in den vorlesungen funktionen immer in der header geschrieben, aber ich kann mich auch täuschen, was sehr wahrscheinlich ist, denn wüsste ich genau was wir gemacht haben wäre ich kaum zwei mal durchgefallen
Ich würde eher auf letzteres Tippen...
FedX schrieb:
es wär super, wenn du mich aufklären könntest, was wo hinkommt.
In Header-Dateien kommen normal nur Deklarationen. Implementierungen und Variablendefinitionen sollten nur in .c sein.
Wenn Du in Deinem Fall die .h-Datei irgendwo anders einbindest, dann hast Du den Code doppelt, was zu Fehlern führt. Sei mir nicht böse, aber alles Andere an Erklärung geht mir zuweit. Kauf Dir ein gutes C-Buch / Tutorial. Würde auch in der Hinsicht für Deine Prüfung sicher nicht schaden.
FedX schrieb:
was ich auch nicht verstehe, warum sind die ersten zeichen von pointern auf einen char immer unterschiedlich ?
wenn ich zweimal einen namen habe, der gleich ist und darauf jeweils einen pointer zeige, dann haben doch beide pointer den wert des namens oder?
Nicht die ersten Zeichen sind unterschiedlich, sondern die Pointer in Deinem Fall. Nachdem Du bei Dir in der Struct keinen Pointer auf einen String hast, sondern den String selbst (char x[20]), können die Adressen auf das erste Zeichen nie gleich werden, weil Du ja immer verschiedene Structs vergleichst.
Wenn Du zweimal einen gleichen Namen hast, dann sind beide Speicherbereiche gleich gefüllt, haben jedoch trotzdem unterschiedliche Adresse. Wenn Du natürlich zwei POINTER (char *x) auf ein und denselben String erstellst, dann sind die Pointer natürlich gleich.
FedX schrieb:
mit strcmp() kann man sicher auch irgendwie eine sortierung vornehmen oder?
Wie sicher in der Hilfe steht gibt strcmp() einen int-Wert zurück, welcher die alphabetische Reihenfolge zueinander wiederspiegelt. Damit kannst Du dann mit einem passenden Algo sortieren.
Dazu gibt es aber sicher etliche Themen und auch Tutorials.
Grüße,
Chriss
-
auch wenn meine dämlichen fragen nervig werden
:
wie könnte ich denn zwei pointer auf 2 integer werte vergleichen?
zb:
int i=2,c=2;intp1;
intp2;p1=i;
p2=c;darf ich dann if(p1==p2) schreiben?
ich glaube ich verstehe bei pointern noch nicht ganz, wann es um die adresse des pointers geht und wann um den wert den der pointer hat.
oder gilt diese ausnahme der vergleiche nur bei strings und wenn ich p1 mit p2 vergleiche vergleicht er wieder die zugeordneten werte wie ich es auch haben will ?
lg
-
int i=2,c=2; int*p1; int*p2; p1=i; !! p2=c; !!
Die Pointer mußt Du so füllen:
p1 = &i; p2 = &c;
Mit dem Code vergleichst Du die beiden Adressen miteinander:
if(p1==p2)
Mit dem Code vergleichst Du den Inhalt der Adressen, also die int-Werte selbst.
if( *p1 == *p2 )
-
gut danke
an den tollen adressoperator habe ich gar nicht mehr gedacht.
lg
-
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 nichtlg
-
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 beantwortetlg
-
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.