Einfach verkettete Listen



  • Hey, ich hab ei paar Probleme mit einfach verketteten Listen. Ich hab leider die Vorlesung verschlafen udn werde jetzt einfach nicht schlau draus.
    Dazu kommt noch, dass ich es mit Zeigern auch nicht so hab.

    Im folgenden Code wollte ich ein Bücherregal anlegen und in 2 Durchläufen mit Büchern füllen, allerdings bricht die Ausführung nach der Eingabe aller Daten ab, weshalb ich vermute, dass es an der gothrough Prozedur liegt.
    Ich würde meinen, es liegt daran, dass wenn ich "Regal" an diese Funktion übergebe, es ja nicht mehr auf den ersten Listenwert zeigt, momentan hab ich aber auch keine Idee, wie es anders geht^^

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    struct book
    {
        char autor[15];
        char titel[15];
        int Ejahr;
        struct book *next;
    };
    
    void add(struct book *aktElement, char *autor, char *titel, int jahr)
    {
        if(aktElement == NULL)
        {
            aktElement = malloc(sizeof(struct book));
            strcpy(aktElement->autor,autor);
            strcpy(aktElement->titel,titel);
            aktElement->Ejahr = jahr;
            aktElement->next = NULL;
        }
        else if(aktElement->next == NULL)
        {
            aktElement->next = malloc(sizeof(struct book));
            aktElement = aktElement->next;
            strcpy(aktElement->autor,autor);
            strcpy(aktElement->titel,titel);
            aktElement->Ejahr = jahr;
            aktElement->next = NULL;
        }
    }
    
    void gothrough(struct book *b)
    {
        struct book *regal;
        regal=b;
        printf ("%s\n", regal->autor);
        printf ("%s\n", regal->titel);
        printf ("%d\n", regal->Ejahr);
        while (regal->next != NULL)
        {
            regal=regal->next;
            printf ("%s\n%s\n%d\n",regal->autor,regal->titel, regal->Ejahr);
        }
    }
    
    int main()
    {
        struct book *regal = NULL;
        char a[15],t[15];
        int j,i;
        i = 0;
        while(i<2)
        {
            fgets(a,15,stdin);
            fgets(t,15,stdin);
            scanf("%d",&j);
            fflush(stdin);
            add(regal,a,t,j);
            i++;
        }
        gothrough(regal);
        return 0;
    }
    


  • Deine gothrough-Funktion ist im Grunde OK, deine add-Funktion ist das Problem. Man kann das auch gar nicht so einfach fixen, weil sie schon schlecht konzipiert ist. Spricht irgendwas gegen eine Funktion, die neue Elemente einfach vorne anhängt?

    struct book* add_front(struct book *root, char *autor, char *titel, int jahr)
    {
         struct book *newBook = malloc(sizeof(struct book));
         strcpy(newBook->autor, autor);
         strcpy(newBook->titel, titel);
         newBook->Ejahr = jahr;
         aktElement->next = root;
         return newBook;
    }
    ...
    regal = add_front(regal, a, t, j);
    

    edit:
    Noch was zu gothrough vergessen. Die Funktion ist natürlich nur im Grunde OK, im Detail aber falsch. Das merkt man schon daran, dass da sinnloserweise zweimal (auch noch verschiedener) Code für das Ausgeben eines Elementes steht. Was ist denn, wenn ich eine leere Liste, dh einen Nullpointer, übergebe?



  • Danke schon mal für die Hilfe!

    Bashar schrieb:

    Deine gothrough-Funktion ist im Grunde OK, deine add-Funktion ist das Problem. Man kann das auch gar nicht so einfach fixen, weil sie schon schlecht konzipiert ist. Spricht irgendwas gegen eine Funktion, die neue Elemente einfach vorne anhängt?

    Also prinzipiell spricht nichts dagegen, vorne anzufangen aber ich würde es trotzdem ganz gerne hinten dran setzen^^

    Bashar schrieb:

    edit:
    Noch was zu gothrough vergessen. Die Funktion ist natürlich nur im Grunde OK, im Detail aber falsch. Das merkt man schon daran, dass da sinnloserweise zweimal (auch noch verschiedener) Code für das Ausgeben eines Elementes steht. Was ist denn, wenn ich eine leere Liste, dh einen Nullpointer, übergebe?

    Wieso denn verschiedener Code? Ich bin nicht sicher was du meinst^^
    Aber ich habs nochmal ein bisschen umgschrieben. Abgesehen davon, dass jetzt leere Liste ausgegeben wird.

    void gothrough(struct book *b)
    {
        struct book *regal;
        regal=b;
        if(regal != NULL)
        {
            while (regal->next != NULL)
            {
                printf ("Autor: %sTitel: %sErscheinungsjahr: %d",regal->autor,regal->titel, regal->Ejahr);
                regal = regal->next;
            }
        }
        else printf("Leeres Regal.");
    }
    


  • Schau dir mal mit dem Debugger oder mit

    printf("Regal: %p\n", regal);
    

    (vor und/oder nach Zeile 60) den Inhalt von regal an.



  • MrMonkey schrieb:

    Wieso denn verschiedener Code? Ich bin nicht sicher was du meinst^^

    EInmal drei Zeilen printf und dann einen Einzeiler

    MrMonkey schrieb:

    Aber ich habs nochmal ein bisschen umgschrieben.

    Die Abbruchbedingung für die Schleife ist falsch.
    So wird das letzte (oder einzige) Element nicht ausgegeben.

    MrMonkey schrieb:

    Abgesehen davon, dass jetzt leere Liste ausgegeben wird.

    Immer?



  • Dein Problem liegt bei add. Du hast call by value nicht verstanden.
    Außerdem funktioniert die add-Funktion nur genau 2x.
    Das ist ziemlich unsinnig für eine Funktion.
    Außerdem fehlt free.

    struct book
    {
      char autor[15];
      char titel[15];
      int Ejahr;
      struct book *next;
    };
    
    void add(struct book **x, char *autor, char *titel, int jahr)
    {
      if (*x)
      {
        while (x[0]->next) x = &x[0]->next;
        x = &x[0]->next;
      }
    
      x[0] = calloc(1,sizeof**x);    
      strcpy(x[0]->autor, autor);    
      strcpy(x[0]->titel, titel);    
      x[0]->Ejahr = jahr;
    }
    
    void gothrough(struct book *b)
    {
      while (b)
      {
        printf("%s\n%s\n%d\n", b->autor, b->titel, b->Ejahr);
        b = b->next;
      }
    }
    
    int main()
    {
      struct book *regal = NULL;
      char a[15], t[15];
      int j, i=0;
      while (i<2)
      {
        scanf("%14s%14s%d", a, t, &j);
        add(&regal, a, t, j);
        i++;
      }
      gothrough(regal);
    /* free für regal fehlt noch */
      return 0;
    }
    


  • Wow, Danke, das funktioniert(wen wunderts)
    Aber ich hab nochmal ein paar Fragen

    Wutz schrieb:

    void add(struct book **x, char *autor, char *titel, int jahr)
    {
      if (*x)
      {
        while (x[0]->next) x = &x[0]->next;
        x = &x[0]->next;
      }
      
      x[0] = calloc(1,sizeof**x);    
      strcpy(x[0]->autor, autor);    
      strcpy(x[0]->titel, titel);    
      x[0]->Ejahr = jahr;
    }
    

    Wozu der Zeiger auf den Zeiger?
    Und könnt man statt x[0] nicht auch (*x) schreiben?
    Und verstehe ich das Prinzip richtig? Man "zählt"(umgangssprachlich) hoch bis zum letzten richtige Element, danach setzt man dieses auf das eigentliche NULL Element für das man dann Speicher allokiert und dann die Werte zuweist?
    Ich hoffe, man versteht mich halbwegs^^

    Wutz schrieb:

    void gothrough(struct book *b)
    {
      while (b)
      {
        printf("%s\n%s\n%d\n", b->autor, b->titel, b->Ejahr);
        b = b->next;
      }
    }
    
    int main()
    {
      struct book *regal = NULL;
      char a[15], t[15];
      int j, i=0;
      while (i<2)
      {
        scanf("%14s%14s%d", a, t, &j);
        add(&regal, a, t, j);
        i++;
      }
      gothrough(regal);
    /* free für regal fehlt noch */
      return 0;
    }
    

    das &regal beim add wegen dem Zeiger auf den Zeiger, richtig?

    Und mal etwas Abseits von dem. In meiner ersten "Lösung" hab ich fflush genutzt, das löscht angeblich den Eingabepuffer aber warum musste ich das eigentlich machen? Die Lösung hatte ich aus dem Internet, weil das Programm beim 2. Durchlauf der Schleife immer vor der Eingabe des Jahres abgeschmiert ist aber warum?



  • MrMonkey schrieb:

    Wozu der Zeiger auf den Zeiger?

    Damit man den Zeiger an sich verändern kann.

    MrMonkey schrieb:

    Und könnt man statt x[0] nicht auch (*x) schreiben?

    Ja.

    MrMonkey schrieb:

    Und verstehe ich das Prinzip richtig? Man "zählt"(umgangssprachlich) hoch bis zum letzten richtige Element, danach setzt man dieses auf das eigentliche NULL Element für das man dann Speicher allokiert und dann die Werte zuweist?

    Jein. Weiß nicht. Was ist "dieses"? x zeigt auf jeden Fall vor Zeile 9 auf einen book* , der das Ende der Liste markiert.

    MrMonkey schrieb:

    das &regal beim add wegen dem Zeiger auf den Zeiger, richtig?

    Ja. Die Adresse des Zeigers selbst wird übergeben.

    MrMonkey schrieb:

    Und mal etwas Abseits von dem. In meiner ersten "Lösung" hab ich fflush genutzt, das löscht angeblich den Eingabepuffer aber warum musste ich das eigentlich machen? Die Lösung hatte ich aus dem Internet, weil das Programm beim 2. Durchlauf der Schleife immer vor der Eingabe des Jahres abgeschmiert ist aber warum?

    Daß fflush() auf irgendeine magische Art irgendwelche Eingabepuffer "löscht" ist ein nicht ausrottbarer Mythos (vielen Dank, MS). Das Verhalten von fflush() auf Inputstreams ist undefiniert. Darf dir auch eine Pizza bestellen, deinen Rechner abfackeln oder nichtstun.
    "Abgeschmiert"? bist sicher? "Leeren" tut man etwas aus dem man lesen kann im allgemeinen indem man ließt, bis nichts mehr zu lesen da ist.



  • MrMonkey schrieb:

    Und mal etwas Abseits von dem. In meiner ersten "Lösung" hab ich fflush genutzt, das löscht angeblich den Eingabepuffer aber warum musste ich das eigentlich machen? Die Lösung hatte ich aus dem Internet, weil das Programm beim 2. Durchlauf der Schleife immer vor der Eingabe des Jahres abgeschmiert ist aber warum?

    Das scanf liest das '\n' von der Entertaste nicht mit ein, da es nicht zu einer Ganzzahl passt (%d).

    Das fgets wiederum liest alles bis zum '\n' (oder erreichen der vorgegebenen Länge) ein.
    Jetzt steht da aber schon das '\n'. Damit ist für fgets die Sache erledigt.
    fgets speichert übrigens das '\n' mit im Buffer ab. Das macht evtl. Probleme, wenn du die Texte vergleichen willst.


Anmelden zum Antworten