Einfach verkettete Liste (C)?
Also in meiner main-Funktion steht das:
customer *root = NULL;
root = createCustomer(1, "Hans", "Maurer");
addCustomer(root, createCustomer(3, "Tatjana", "Roth"));
printList(root);
Mein Struct und createCustomer-Funktion sehen so aus:
typedef struct customer{
char vorname[20];
char name[20];
int kundennummer;
struct customer * next;
}customer;
customer * root = NULL;
customer* createCustomer(int zahl, char* vorname, char* name)
{
customer * neu = (customer*)malloc(sizeof(customer));
if(neu==NULL)
{
exit(-1);
}
strcpy(neu->vorname, vorname);
strcpy(neu->name, name);
neu->kundennummer = zahl;
neu->next = root;
root = neu;
return root;
}
Ich glaube das sollte soweit stimmen, aber ich weiß nicht wie ich die addCustomer-Funktion schreiben soll. Ich habe vieles schon probiert, aber nichts klappt. Ich wäre für jede Hilfe dankbar. Liegt es vllt. an meiner printList-funktion?
void printList(customer* root)
{
customer * ptr = root;
while(ptr!=NULL)
{
printf("%d: %s %s\n", ptr->kundennummer, ptr->vorname, ptr->name);
ptr = ptr->next;
}
}
1 Antwort
Dein Problem ist, dass du die Lösung für "addCustomer" bereits in "createCustomer" geschrieben hast.
Folgendes muss in "addCustomer":
neu->next = root;
root = neu;
Natürlich musst du beide Methoden (z.B. den Rückgabewert von "createCustomer") entsprechend anpassen.
Okay, die Aufgabenstellung ist Mist. (Zumal sie auch an einer Stelle "insert" nennt, dann aber "addCustomer" verwendet.)
Denn wenn "addCustomer" einen "customer*" auf das Root-Element erhält, dann kann man das Root-Element nicht austauschen.
"createCustomer" soll aber vermutlich ein Listenelement zurückgeben.
Im Endeffekt heißt das, wenn das Root-Objekt nicht passend einsortiert ist, dann funktioniert es nicht.
Du kannst aber etwas tricksen, damit es doch geht:
Du lässt "createCustomer" kein listenelement zurückgeben, sondern einen Wrapper, quasi einen Pointer auf ein Listenelement.
"customer*" wäre dann ein Pointer auf einen Pointer auf ein Listenelement.
Dann erhält "addCustomer" zwei solche Pointer, wodurch du den Pointer von "root" auch umändern kannst.
Das ganze sorgt allerdings dafür, dass du an allen anderen Stellen zweimal dereferenzieren musst, damit alles funktioniert.
Evtl. wäre es besser, stattdessen die Aufgabenstellung abzuändern, sofern das denn erlaubt ist. Du machst aus
addCustomer(root
ein
addCustomer(&root
und änderst die Methode-Signatur von "addCustomer" entsprechend.
Ich denke das wäre auch sinnvoller, denn einerseits passt die Main-Methode ja sowieso nicht zur Aufgabenstellung, andererseits dient diese ja auch nur zum Testen.
Leider kann bzw. darf ich die Aufgabenstellung nicht ändern.
Ich sag dir ehrlich ich hab obere kaum verstanden :D
Nun, dann musst du es mit dem Dummy machen. Oder du wendest dich an den Aufgabensteller und weist auf die Widersprüche hin.
typedef element** customer*;
"element" ist dann dein eigentliches Listen-Element.
Anpassen musst du entsprechend die anderen Stellen im Code, insbesondere Allokation (du musst "element" allokieren und "customer*", ebenso auch bedes freigeben.
Dein "element"-Struct würdest du dann entweder su bauen, dass es "element*" enthält oder "element**". Letzteres sorgt für mehr Indirektion, ersteres sorgt dafür, dass du nur bei root zusätzliche Indirektion hast und bei allem, was aus "createCustomer" rausfliegt, dafür musst du aber bei "addCustomer" dann einmal den Pointer entpacken und freeen.
Ich sag dir ehrlich ich hab obere kaum verstanden :D
Nun, das kann ich nachvollziehen.
Eigentlich ist es aber einfach:
Statt einen Pointer verwendest du einen Pointer auf einen Pointer. Du hast dann eine Indirektion mehr, musst als öfters dereferenzieren und eine Speicherstelle jeweils mehr managen.
Das ist ja kompliziert hoch 10... Ich weiß nicht ob ich das packe. Weil meinst du das ich meinen struct ändern soll mit dem "typedef element* customer"?
Vor allem mein Code funktioniert (außer die Sortier-Funktion) weil ich gar nicht erst root benutze sondern einfach meinen Anker. Ich möchte jetzt eigentlich nur noch die Sortier-Funktion schreiben und so dann abgeben. Das was du grade erklärt hast ist viel zu kompliziert für mich...
Kann ich dir mal mein Code schicken, damit du dann gucken kannst ob das so in Ordnung wäre? Auf das root werde ich lieber mal drauf scheißen in gut deutsch :D
Weil meinst du das ich meinen struct ändern soll mit dem "typedef element* customer"?
Dein Struct benennst du um in "element". Nach dem Struct fügst du das tpedef ein.
Das Typedef definiert dann quasi den Typ "customer" als Alias für "element*".
Vor allem mein Code funktioniert (außer die Sortier-Funktion) weil ich gar nicht erst root benutze sondern einfach meinen Anker. Ich möchte jetzt eigentlich nur noch die Sortier-Funktion schreiben und so dann abgeben. Das was du grade erklärt hast ist viel zu kompliziert für mich...
Schuld daran ist die Aufgabenstellung, die falsch ist. Eigentlich müsste man die anpassen, dann müsstest du deinen Code auch nur leicht verändern.
Dein derzeitiger Code ist ja schon falsch, da er die Methoden nicht wie beschrieben implementiert, da das ohne das komplizierte, was ich oben beschrieb, garnicht geht.
Die Sortierfunktion kannst du evtl. auch so implementieren, je nachdem, was das für ein Anker ist. Der Anker selbst dürfte aber wohl bereits gegen die Aufgabenstellung verstoßen.
Ansonsten kannst du die Sortierfunktion nicht implementieren ohne eine der durch mich genannten Änderungen, denn du musst in der lage sein können, das root-Element auszutauschen.
Ja das ist leider nicht möglich, ich muss die Aufgabe schon Sonntag abgeben und der drückt auch mal Augen zu
Wenn der Augen zudrückt (muss er ja auch, sonst würden vermutlich alle Studenten durchfallen bei der Aufgabenstellung), dann verwende die einfache Anpassung, die ich beschrieben hatte. Dann kannst du "addCustomer" passend implementieren und auch die Sortier-Funktion.
Oder du baust den Anker als
customer**
und verwendest den statt root, das kommt dann auf dasselbe raus, nur, dass du etwas tricksen musst, um das erste Element hinzuzufügen.
Welche genau? :D Wir haben so viel geschrieben
Werde ich mal versuchen, weiß aber nicht ob ichs schaffe. Trotzdem vielen Dank für deine Hilfe.
Aber wie benutz ich den root als anker? Oder soll ich immernoch meinen anker benutzen?
Je nachdem, was konkret du denn jetzt machen möchtest.
Wenn du die "&root"-Lösung verwendest, dann schmeißt du deinen Anker weg und managst das Hinzufüguen allein in "addCustomer".
"addCustomer" erhält als erstes Parameter dann einen "customer**" auf den Root-Pointer, wodurch du den Root-Pointer, wenn nötig, austauschen kannst.
Also benutz dann noch createCustomer? Wenn nein ändere ich die Aufgabe ja schon sehr in dem ich ganze Funktionen weglasse
Weil ich hab das so gelernt dass das nur mit nem Anker geht
In der Main-Methode steht doch:
customer *root = NULL;
root = createCustomer(1, "Hans", "Maurer");
Aber ich kann doch nicht root in createCustomer benutzen da es nicht global definiert wurde.
Musst du ja auch nicht, denn da steht ja:
root = createCustomer(1, "Hans", "Maurer");
Entsprechend wird der Rückgabewert des Aufrufs von "createCustomer" der Variable "root" zugewiesen.
Wie gesagt, "createCustomer" soll nichts wieter tun als das neue Customer-Objekt zu erstellen, zu initialisieren und zurückzugeben.
Also nur das?
customer* createCustomer(int zahl, char* vorname, char* name)
{
customer * neu = (customer*)malloc(sizeof(customer));
if(neu==NULL)
{
exit(-1);
}
strcpy(neu->vorname, vorname);
strcpy(neu->name, name);
neu->kundennummer = zahl;
Aber was kommt dann als rückgabewert?
Habe leider keine Zeit weder Lust :D nach diesem ganzen Schlamassel
Also wenn du dich anstrengst bekommst du das mit dem Sortieren vielleicht noch hin bis morgen.
Könnte aber schwer werden, soweit ich das verstehe fehlen dir ein paar Grundverständnisse bezühlich C. (Aber vielleicht kommen die noch mit der Zeit, C ist etwas verwirrend anfangs wegen der ganzen Pointer und Fallstricke.)
Ja C ist wirklich schwer... Bei mir kommt jetzt das als ergbenis:
Momentan sind 3 Kunden erfasst.
0:
0:
1: Hans Maurer
Momentan sind 0 Kunden erfasst.
Das ist schoneinmal gut, dann passt die "createCustomer"-Methode.
Als nächstes implementierst du die "addCustomer"-Methode, am Besten ersteinmal so, dass ganz am Ende das neue Element eingefügt wird (da du den Code zum durchiterieren der Liste, den du dafür brauchst, beim sortieren später noch verwenden musst).
Falsch ist daran nichts, nur hast du halt erst einen teil der Aufgabe erledigt.
Ich habe bereits alle Methoden aber bei einer funktioniert wohl etwas nicht...
Sieht die addCustomer-Methode gut aus soweit?
void addCustomer(customer** root, customer* neuKunde)
{
customer * neu = (customer*)malloc(sizeof(customer));
if(neu==NULL)
{
exit(-1);
}
neu->next = *root;
*root = neu;
}
Das hier muss weg, das hast du schon in "createCustomer":
customer * neu = (customer*)malloc(sizeof(customer));
if(neu==NULL)
{
exit(-1);
}
Ansonsten sieht es brauchbar aus für einen ersten Entwurf von "addCustomer", nur musst du unten das "neu" durch "neuKunde" ersetzen.
Du willst ja den übergebenen Kunden hinzufügen und keinen neuen erstellen.
Omg ja jetzt klappt es. Jetzt brauch ich nur noch die Sortier-Funktion. Vielen Dank für deine Hilfe :D Du warst mir ne größere Hilfe als meine Professorin
Nicht "nur noch". Das ist noch ein großes Stück arbeit.
Schreib ersteinmal die "count"Funktion, dann hast du es später beim sortieren einfacher.
Und dass dein Professor keine große Hilfe ist, nun ja, das erschloss sich bereits, sonst wären deine Fragen hier unnötig (und evtl. die Aufgabenstellung besser).
Das ist meine Count-Funktion:
int customerCount(customer* root)
{
int count=0;
customer * ptr = root;
while(ptr!=NULL)
{
ptr = ptr->next;
count++;
}
return count;
}
Sieht das gut aus?
Ne das stimmt so weil ich die Liste zum Schluss nochmal lösche bzw. muss ich die auch löschen
Aber was genau bringt mir die Count-Methode jetzt für die Sortier-Methode?
Ne das stimmt so weil ich die Liste zum Schluss nochmal lösche bzw. muss ich die auch löschen
Ja, hab's gesehen und korrigiert.
Aber was genau bringt mir die Count-Methode jetzt für die Sortier-Methode?
Bei der Sortier-Methode musst du dich auch durch deine Liste durchhangeln.
Und dann eben das neue Element an der passenden Stelle einfügen.
Hmm ich glaub das wird ne Geburt das zu machen... Hast du evlt. ne gute Seite wo ich mir das anschauen kann?
Googel gibt dir da sicher etwas.
Oder der Link, den ich vorher hier gepostet hatte, hilft auch.
Und ansonsten ist es eigentlich intuitiv, Schleifenbedingung passend wählen und Element ersetzen, wenn gefunden. Dann noch Sonderfälle beachten (Anfang der Liste, Ende der Liste, Liste leer, ...) und fertig.
Ja bei einer normalen Schleife ist das aber irgendwie viel einfacher als hier :D
Ja das wird ne Geburt... Kann ich dir dann später schreiben wenn ich Hilfe brauche?
Also ich habs gestern Ewigkeiten angeguckt wie das geht etc. aber ich habe nicht mal ein Zeile Code hingekriegt. Ich weiß wirklich nicht wie ich das angehen soll
Versuch ich doch:
void addCustomer(customer** root, customer* neuKunde)
{
if(*root = NULL) Was ist hier falsch?
{
neuKunde->next = NULL;
*root = neuKunde;
}
else
{
customer* ptr = *root;
while(ptr->next != NULL)
{
ptr = ptr->next;
}
ptr->next = neuKunde;
neuKunde->next = NULL;
}
}
Das sollte soweit passen, gut.
Als nächstes baust du das jetzt so um, dass das Element an der vorletzten Stelle eingefügt wird, statt an der letzten bzw. an der Stelle wo am Ende "ptr" steht. Dazu brauchst du einen weiteren Sonderfall für "root".
Dann brauchst du danach nur noch das Element mit dem aktuellen zu vergleichen und es einfügen, wenn es kleiner/größer ist.
Hört sich irgendwie sehr kompliziert an :D Ich hab ka wie ich das machen soll
Ist garnicht kompliziert. So, wie gerade auch schon, nur mit einem Drag-Pointer.
Alternativ kann man das aber auch mit einer etzwas komplizierteren Schleifenbedingung machen, dann braucht es aber auch den Sonderfall für das Root-Element.
Entsprechend, bau am besten ersteinmal den.
Wie fügt man das Element an die vorletzte Stelle? An die letzte Stelle war ja irgendwie machbar aber das...
Mach am besten ersteinmal folgendes:
Wenn das root-Element größer gleich (letzteres, um eine bessere Performance zu erhalten, damit die Liste nicht für gleiche Elemente durchlaufen wird) als das neue Element ist, dann setzt du das neue Element an die Stelle des root-Elements und hängst das Root-Element an das neue Element an.
Und im nächsten Schritt erweiterst du das dann für die weiteren Elemente. Dafür brauchst du einen Drag-Pointer, also einen Pointer, der auf das Element vor dem aktuellen zeigt. (Den kannst du ganz einfach in der Schleife immer auf das letzte "ptr" setzen oder dergleichen.)
Bei mir kommt aber der Fehler bei der if-Abrage:
‘*root’ is a pointer; did you mean to use ‘->’?
else if(*root->kundennummer>neuKunde->kundennummer)
^~
->
Doch die Klammern haben funktioniert. Aber warum klappts nicht ohne Klammern.
Und ist der Code richtig (der der in der if-Abfrage stehen soll):
neuKunde = *root;
*root = neuKunde->next;
Aber warum klappts nicht ohne Klammern.
ich vermute, da der das automatisch so klammert:
*(root->kundennummer)
Und das ist eben falsch.
Und ist der Code richtig (der der in der if-Abfrage stehen soll)
Nein. Du überschreibst das Element, das du einfügen möchtest, das kann nicht richtig sein.
Habe ich damit nicht neuKunde an roots stelle gesetzt und root an die Stelle von neuKunde
Ja stimmt schon. Und wie hänge ich jetzt das alte root an die Stelle?
Werd ich mal versuchen. Aber ändert sich dann an der Ausgabe etwas?
Ja gut, dann weiß ich ja nicht unbedingt ob das richtig ist außer ich hab das vollkommen verstanden
Ich glaube das wird nichts mehr.
Versuch es dir hinzuzeichnen und wenn es dann nicht klappt, lässt du das mit dem Programmieren evtl. bleiben oder fängst mit einer leichteren Programmiersprache an.
Wobei du den Rest ja auch hinbekommen hattest, vielleicht brauchst du schliht mehr Zeit?
Ja mehr Zeit habe ich aber leider nicht... Ich muss die Aufgabe heute schon abgeben.
Ja wann anders wäre schön aber die Abgabe ist ja entscheidend
Ja ich mal lieber ne Pause. Das Zeichen werde ich auch nochmal probieren
Danke für deine Hilfe. Das ist wirklich nicht selbstverständlich, deswegen ist mein Dank umso größer.
Ja wenn ich das machen kommt aber raus das ich 0 Kunden habe
Leider weiß ich nicht wie das genau geht. Aber trotzdem danke für die Hilfe
Hier die Aufgabenstellung:
Erweitern Sie die einfache Kundenverwaltung folgendermaßen:
Mit der gegebenen main()-Funktion können Sie Ihre Funktionen testen.
Erwartete Ausgabe:
und dann hab ich noch die Main-Funktion:
customer *root = NULL;
root = createCustomer(1, "Hans", "Maurer");
addCustomer(root, createCustomer(3, "Tatjana", "Roth"));
addCustomer(root, createCustomer(2, "Anna-Maria", "Schmidt"));
printf("Momentan sind %u Kunden erfasst.\n", customerCount(root));
printList(root);
clearList(root);
root = NULL;
printf("Momentan sind %u Kunden erfasst.\n", customerCount(root));
printList(root);