Liste => erste Versuche



  • Sieht soweit richtig aus.
    Ja, auch das freigeben.

    You make my day :-))

    Jetzt aber alles mal in getrennte Funktionen.

    Ja, das werd ich machen. Dazu aber vorab :

    Ich muss einer Funktion zum "erstellen" , immer einen Zeiger auf das vorgehende Element übergeben, um es dahinter einfügen zu können ?? Oder ist es besser einfach einen Zeiger auf "Listenanfang" zu übergeben und solange die Liste zu durchsuchen bis ich "NULL" gefunden habe, und dann einfügen?
    Generell... wäre es sinnvoll , wenn die Funktion nen Zeiger auf das eingefügte Element zurückgibt?

    Bezüglich des Free nochmal. In einem anderen Thread wurde das schonmal angeschnitten, unter Linux deckt Valgrind wohl Speicherlecks usw. auf.
    Ich benutze haupsächlich Win7 mit DevC++ (geht auf Arbeit nicht anders).... Gibt's da irgendeine möglichkeit da was zu prüfen? =>v1 gab keine Speicher frei, stürzte aber auch nicht ab. Ich war mir hald ziemlich sicher das es NICHT passt, aber eine Prüfung wäre nicht schlecht....

    =>Das verkettete Listen in der Praxis nicht wirklich oft vorkommen wurde schon besprochen... Weiterhin wäre aber noch interssant.. Baumstrukturen usw usw... Nice to know oder essential element??

    ...mir viel das bei dynamsichen Speicherreservierungen schon auf, das ich das für meine "pillepalle" Prog´s wohl nie wirklich sinnvoll nutzen werde, höchstens um in Übung zu bleiben, da ich fast immer weiß wieviel Speiche ich brauche, oder den Puffer groß genug mache. Aber da geht´s ja auch um nix 🙂



  • Elemente werden nicht nur am Ende angehängt sondern auch mal in der Mitte eingefügt.
    Auch kann das neue Element am Anfang der Liste stehen.

    Daher braucht die Funktion zum einfügen beide Informationen. Den Listenanfang und das Element hinter dem eingefügt werden soll.
    Dabei musst du dann darauf achten, dass du den Listenanfang neu setzt, bzw. dass neu->next dann nicht mehr NULL ist.

    beginner_of schrieb:

    Generell... wäre es sinnvoll , wenn die Funktion nen Zeiger auf das eingefügte Element zurückgibt?

    Warum? Die Funktion besorgt ja nicht den Speicher für das neue Element. Die hängt das nur in die Liste ein. Speicher besorgen und mit Werten besetzen macht eine andere Funktion.

    Die Funktion kann den neuen Zeiger auf den Listenanfang zurück geben (dann sparst du dir Doppelzeiger).

    Dein Programm v1 lief zu kurz und hat zu wenig Speicher verbraucht. Durch Speicherlecks verlierst du Speicher. Das System hat irgendwann keinen freien Speicher mehr und/oder muss auslagern.
    Moderne Systeme geben den Speicher beim Beenden eines Programms auch wieder frei.



  • Daher braucht die Funktion zum einfügen beide Informationen. Den Listenanfang und das Element hinter dem eingefügt werden soll.

    ...Sehe ich richtig, das dies dann beim ersten Eintrag, bzw. beim neu erstellen der Liste beides der gleiche Zeiger ist..??
    Aufbau, bzw. sinngemäß so :

    erstellen(element* anfang, element*hier_einfügen)
    

    ???

    Die Funktion kann den neuen Zeiger auf den Listenanfang zurück geben (dann sparst du dir Doppelzeiger).

    ähm... kann=> Ich sag jetzt mal ich hatte nicht vor den Listenanfang zu ändern oder verstehe ich das jetzt falsch?



  • Was machst du, wenn du eine leere Liste hast? Dann zeigt liste auf NULL.
    Was ist, wenn du ein neues Element vor dem Ersten eintragen willst?

    Dann musst du den Zeiger auf den ersten Eintrag ändern.

    beginnger_offl schrieb:

    erstellen(element* anfang, element*hier_einfügen)
    

    Da fehlt was. Zudem solltest du doch erstellen und einfuegen trennen.

    struct liste *erstellen(char c, int v);
    // besorgt Speicher und kopiert die Daten da rein
    
    struct liste *einfuegen(struct liste *list, struct liste *prev, struct liste *neu);
    //list = Zeiger auf den Anfang
    //prev = Zeiger auf das Element hinter das eingefügt werden soll
    //neu  = Zeiger auf das Element, das eingefügt werden soll (bekommst du von erstellen)
    // Zurück geht der Zeiger auf die Liste
    


  • bin noch nicht weiter, hab etwas Stress, werde wohl erst am Montag weitermachen.
    Danke für die "Prototypen", das hat /wird mir schonmal helfen.

    zu:

    // Zurück geht der Zeiger auf die Liste

    ... die eingefügt wurde, oder?



  • beginner_OF schrieb:

    ... die eingefügt wurde, oder?

    Du fügst nur ein Element/Knoten in die Liste ein.
    i.A. wird das der Wert vom ersten Paramter sein. Aber wenn du etws vor dem ersten Knoten einhängst, dann ändert sich der Anfang der Liste.

    Das ist so ähnlich wie bei realloc.

    Mal dir das mal auf ein Platt Papier auf. Da kannst du die Zeiger durch Pfeile zwischen den Knoten darstellen.
    Dann kannst du das einfügen und anhängen durchspielen.



  • WAAAHHHH!!!!
    Leute ich kriegs nicht gebacken mit der ****Liste.
    Ich hab momentan nicht so den Kopf dafür, aber ich komm echt nicht drauf.

    Also erstellen müsste noch passen :

    #include <stdio.h>
    #include <stdlib.h>
    #include "sys.h"
    
    Liste *erstellen(char c, int v){
    
    Liste *tmp;
    
    tmp=malloc(sizeof(Liste)); 
    
    if(!tmp){
          puts("Error Mallocation");
          exit(1);
          getchar();
       }
    
    tmp->ival=v;
    tmp->cval=c;
    tmp->next=NULL;
    
    return tmp;
    
    }//END
    

    Dann hab ich die Funktion "einfügen"
    ... ich glaube festgestellt zu haben (die debugs und Kommentare vernebeln mich mittlerweile...) das das Return falsch ist??

    #include <stdio.h>
    #include <stdlib.h>
    #include "sys.h"
    #define debug
    
    Liste *einf_1(Liste *list,  Liste *neu){
    /* einf_1 fügt ein Element in die Liste "hinten" ein. return Zeiger auf Anfang, *list = Anfang*/
    
    Liste *einf, *tmp=NULL, *start ;
    
    start=list;
    
    if (list == NULL){
             #ifdef debug
             puts("List Empty");    // Liste ist leer  
             #endif       
             list = neu;          // das neue Element ist das 1. Element              
             return list;      // einf_1 verändert den Anfang aber nicht....
        }
    
     //Ende suchen 
    
       einf=list;      
    
      while (einf != NULL){       // ist "nächstes" Element NULL?       <========|
            tmp=einf->next ;      //tmp zeigt auf das element nach einf          |
            #ifdef debug          //                                             |
            puts("here");          //                                            |
            #endif                //                                             |
            einf=tmp;            // jetzt zeigt einf auf das nächstes element    |
            }  
     // ENDE gefunden    einf zeigt auf Null
    
          einf=neu;      // neues "Element" hinten anhängen 
          #ifdef debug
          printf("%c  %d  \n" , einf->cval, einf->ival);
          #endif
          return list;
    
    }//END
    

    das ganze wird so aufgerufen :
    start.c

    #include <stdio.h>
    #include <stdlib.h>
    #include "sys.h"
    #define debug 
    
    void start (void){
    
    Liste *daten=NULL, *tmp=NULL, *anfang=NULL;  // daten ist meine Liste ::  tmp mein Puffer :: anfang wird bei einf_1 noch nicht benötigt  
    char c='A';
    int  v=1, create=0, dummy, i=0;
    
    puts("Welcome to the First Version of Linked List's!!!\n");
    
    do{
        printf("How many Elements to Create?"); 
        dummy=scanf("%d", &create); 
        keyb;   
    }while(dummy!=1);
    
    printf("\nOK, Let's make a List with %d - Elements\n", create);
    
    //debug
    Liste *s1, *t1;
    while (i++ <create){ 
    
             tmp=erstellen(c, v);
             c++; v++;                 
             daten=einf_1(daten, tmp);
             #ifdef debug
              s1=daten; 
              printf("daten c : %c    daten i: %d  \n", s1->cval, s1->ival);
              t1=s1->next;
              s1=t1;          
             #endif  
    
         }
    
    }//END
    

    Tja, was geht noch ab:
    ...In der sys.h ist folgendes

    #include <stdlib.h>
    #include <stdio.h>
    #include <string.h>
    #define nl puts("\n")
    #define keyb  while(getchar() != '\n'); 
    
    struct mix{
                char cval;
                int ival;
                struct mix *next;
                };
    
    typedef struct mix Liste; 
    
    void start (void);
    
    Liste *erstellen(char c, int v);    
    
    Liste *einf_1(Liste *list,  Liste *neu);        
    
    void show(Liste *ptr);
    

    und die Main ruft einfach nur start() auf;

    Ich glaub ich hab ein riesen Brett vorm Kopf, ich hab's mir schon x - mal aufgezeichnet aber komm nicht drauf....

    Ich hatte die Funktion show schon eingebunde, funktioniert nicht, da die Liste nicht verkettet wird....



  • Wo meinst du denn, dass du die Liste verkettest?

    Überleg dir mal, welcher Knoten auf welchen zeigt.
    Und ob ein Zeiger auf NULL, auf einen gültigen Knoten zeigt.

    Und du kannst mehrere Zeiger auf dasselbe Objekt haben.
    Wenn du einen davon änderst, ändern sich die anderen aber nicht.

    Zeigerwerte (Adressen) kannst du dir mit %p bei printf anzeigen lassen



  • Wo meinst du denn, dass du die Liste verkettest?

    Naja, wohl nirgends, sonst würde es annähernd funktionieren.
    Also auf in die nächste Runde.
    Danke erstma



  • Schau mal bei einf_1 in Zeile 32: // ENDE gefunden einf zeigt auf Null

    Da bist du schon zu weit. Das einf an der Stelle hat keine Verbindung mit dem letzten Knoten.

    Wenn einf->next == NULL bist du mit der Schleife fertig.
    Dann kannst du einf->next=neu; machen.



  • 😡 😡 Ich bräuchte jetzt einen Smiliy der mit dem Kopf auf den Tisch haut. Mehrmals... bis es richtig weh tut.

    Ok: Jetzt siehts besser aus:
    einf_1.c ( Ich will erst mal nur hinten einfügten, für die advanced Version hab ich dann noch ne Frage...)

    #include <stdio.h>
    #include <stdlib.h>
    #include "sys.h"
    //#define debug
    
    Liste *einf_1(Liste *list,  Liste *neu){
    /* einf_1 fügt ein Element in die Liste "hinten" ein. return Zeiger auf Anfang, *list = Anfang*/
    
    Liste *einf, *tmp=NULL  ;
    
    if (list == NULL){
             #ifdef debug
             puts("List Empty");    // Liste ist leer  
             #endif       
             list = neu;          // das neue Element ist das 1. Element              
             return list;      // einf_1 verändert den Anfang aber nicht....
        }
    
     //Ende suchen 
    
       einf=list;      
    
      while (einf->next != NULL){      //Ist das nächste Element "leer"? einf quasi "Such" - Zeiger
            tmp=einf->next ;           // Nein? Zeiger auf nächstes Element speichern
            #ifdef debug                                                        
            puts("here");          
            #endif               
            einf=tmp;                 // und in meinen "Such"-Zeiger laden
            }  
     // ENDE gefunden    einf->next  zeigt auf Null
    
          einf->next=neu;      // neues "Element" hinten anhängen 
          #ifdef debug
          printf("%c  %d  \n" , einf->cval, einf->ival);
          #endif
          return list;
    
    }//END
    

    Anzeigen lass ich mit

    show.c

    #include <stdio.h>
    #include <stdlib.h>
    #include "sys.h"
    
    void show(Liste *ptr){
    
         Liste *tmp, *show;
    
         show=ptr;
    
         while( show != NULL){
                   printf("Char Value :: %3c      Int Value :: %3d\n", show->cval, show->ival);
                   tmp=show->next;
                   show=tmp;
              }
    
    }//END
    

    An den anderen Files hab ich nicht´s geändert. Also es funktioniert jetzt . Ich denke das das "hinten" anfügen jetzt stimmt??



  • Also wenn die Version so passt, bezw. nicht's mehr "grob fahrlässig" ist... ???

    Nur eine Frage...

    while (i++ <create){
    
             tmp=erstellen(c, v);
             c++; v++;                
             daten=einf_1(daten, tmp);
    

    Meine Funktion Einf_1 ändert ja den anfang der Liste "daten" nicht... würde dann nicht :

    tmp=erstellen(c++, v++)
    einf_1(daten, tmp)
    

    ..reichen? Muss ich unbedingt wenn ich einen Wert zurückgebe, diesen auch benutzen in diesem Fall? Nicht oder?

    Frage zu Advanced: (Prototyp ist jetzt nicht gaaanz auf meinem Mist gewachsen 😉 )

    List *einf_adv(List *list, List *insert, List *neu);
    //*list= Zeiger auf Anfang, 
    //*insert = "dahinter" wird eingefügt
    //*neu = mein neues Element
    //und Zurück geht wieder Zeiger auf die Liste, bzw. den Anfang
    

    ==> *insert was muss ich beachten? Also sagen wir mal ich will nach C :: 3 einfügen ( Meine Elements sind ja ein Char und ein int), also schreib ich mir erstmal eine Suchfunktion, um das Element mit diesem Inhalt zu finden.
    Dannk kann ich den Zeiger auf das "gefundene" Element en meine Funktion einf_adv übergeben.
    Die Vorgehensweise passt so, oder würdet Ihr das anders machen??



  • beginner88888 schrieb:

    ..reichen? Muss ich unbedingt wenn ich einen Wert zurückgebe, diesen auch benutzen in diesem Fall? Nicht oder?

    Nutz du den Rückgabewert von printf, oder strcpy oder scanf?

    beginner88888 schrieb:

    ==> *insert was muss ich beachten? Also sagen wir mal ich will nach C :: 3 einfügen ( Meine Elements sind ja ein Char und ein int), also schreib ich mir erstmal eine Suchfunktion, um das Element mit diesem Inhalt zu finden.
    Dannk kann ich den Zeiger auf das "gefundene" Element en meine Funktion einf_adv übergeben.
    Die Vorgehensweise passt so, oder würdet Ihr das anders machen??

    Ja.

    Und wenn du bei insert NULL übergibst, fügst du am Ende ein. Dann hast du nur eine Funktion.



  • Nutz du den Rückgabewert von printf, oder strcpy oder scanf?

    Kommt drauf an , scanf schon... 😉

    Ok, danke derweilen, der REst passt, ja?



  • Sorry, war Käse.

    tmp=erstellen(c++, v++)
    einf_1(daten, tmp)
    

    funktioniert ja gar nicht, weil ich in einf_1 ja nen Zeiger auf die "Bearbeitete" Liste zurück gebe. War Mist.

    Der Rest passt , ja?



  • beg_ofl schrieb:

    funktioniert ja gar nicht, weil ich in einf_1 ja nen Zeiger auf die "Bearbeitete" Liste zurück gebe. War Mist.

    Ich dachte, dass dir das klar war und es nicht mehr um das erste Element geht sondern um die weiteren.



  • Wollte eigentlich mit der advanced Version weitermachen, aber jetzt hab ich etwas gefunden was mir nicht ganz einleuchtet.

    Also ich habe folgende Funktion zum Löschen der gesamten Liste:

    void clear_all(Liste *list){   
    Liste *tmp;
    
     while(list){
                  tmp=list->next;
                  free(list);
                  puts("del");
                  list=tmp;
                  }
    
    }//END
    

    ABER:
    In meinem Programm mach ich jetzt

    show(daten);
    clear_all(daten);
    //und zum prüfen: 
    show(daten)  //=> nur das erste "Element" ist gelöscht, bzw. da steht jetzt irgendwas drin, das passt.
    // Aber die nächsten Elemente sind noch genau so wie vor clear_all....
    

    Wenn ich clear_all anders mache, z.B.:

    Liste *clear_all(Liste *list){
    //gleicher Code
     retrun list;
    .....
    
    // und im Aufruf : 
    show(daten);
    daten=clear_all(daten);
    show(daten);
    
    //dann passt alles... But why?
    

    Der Speicher wird in beiden Fällen frei gegeben... Ob ich jetzt zurück gebe oder nicht... Warum "löscht" die erste Version nur den ersten Eintrag?? 😮 😕



  • beginner88888 schrieb:

    In meinem Programm mach ich jetzt

    show(daten);
    clear_all(daten);
    //und zum prüfen: 
    show(daten)  //=> nur das erste "Element" ist gelöscht, bzw. da steht jetzt irgendwas drin, das passt.
    // Aber die nächsten Elemente sind noch genau so wie vor clear_all....
    

    Hältst Du es denn nicht für einen Benutzerfehler, show() aufzurufen mit einem Zeiger, den der Benutzer gerade selber freigegeben hat? show() weiss doch davon nix und tut stur seine Arbeit...was da auch immer gerade im Speicher steht.



  • Von löschen hat ja keiner etwas gesagt.
    Die Speicherbereiche werden frei gegeben. D.h. ein anderes Programm kann diesen Speicher nutzen.
    Die Daten die da drin stehen, werden ja nicht venichtet (oder überschrieben).
    Es ist deine Sache als Programmierer dies zu tun.

    Warum klappt die zweite Version?. Dann schau dir mal den Wert von daten an (neue und alte Version)



  • Die Speicherbereiche werden frei gegeben. D.h. ein anderes Programm kann diesen Speicher nutzen.
    Die Daten die da drin stehen, werden ja nicht venichtet (oder überschrieben).

    Ja schon, aber der erste Eintrag geht ja auch flöten. Zufall? bzw. Der Speicher vom ersten Eintrag wird dann wohl schon überschrieben.

    Das war jetzt etwas verwirrend. Aber ich glaub ich weiß jetzt was du meinst.

    Warum klappt die zweite Version?.

    Naja, weil daten dann ganz woanders hinzeigt, und da "nix kommt"?? Schon oder?
    (show hat sowas a`la (!ptr) puts("List Empty") return ).

    Ist dann version 1 oder version 2 "richtiger" ?


Anmelden zum Antworten