Liste => erste Versuche



  • 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" ?



  • ...bzw. Ich ja NULL zurück gebe.



  • Furble Wurble schrieb:

    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.

    Ja, schon. Hab das nicht bedacht, das "nur" weil der Speicher freigegeben wurde, show deswegen ja auch seine arbeit macht...



  • beginner_offl schrieb:

    Furble Wurble schrieb:

    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.

    Ja, schon. Hab das nicht bedacht, das "nur" weil der Speicher freigegeben wurde, show deswegen ja auch seine arbeit macht...

    Da es in dem anderen Thread darum ging noch ein Beispiel für undefined behavior:

    — The value of a pointer that refers to space deallocated by a call to the free or realloc function is used (7.22.3).

    Klingelt da was bezüglich Deiner ersten Alternative?

    Ich sage nicht dass die zweite besser ist. Aber den Pointer, den Du in clear_all() reinsteckst kannst Du danach nicht ohne weiteres verwenden (z.B. in show() )



  • Diesbezüglich sind beide Versionen gleich schlecht.
    Wie ist das eigentlich genau mit der NULL.... Wenn ich in meiner Funktion clear_all am Einde ein

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

    machen würde... Das ändert nichts daran das ich show nicht benutzen darf, oder?
    Also NULL weißt keinen Speicher oder so zu, er zeigt einfach nur auf , ja NULL hald. Oder hat NULL eine Adresse, sprich auch Speicher....hä?

    Also ist jetzt nicht so das der speicher von List zwar freigegeben wurde, der Pointer aber durch das NULL setzten wieder "gültig ist" ?



  • Huh? Manchmal könntet Ihr Kerls Euch wirklich mehr Mühe beim formulieren geben... 😞

    Diese "auf NULL setzen"-Technik sieht man hin und wieder (allerdings müsste dann die Signatur von clear_all() anders sein - nämlich ein Liste** als Parameter).
    In Deinem Fall wäre das sogar die richtige Ergänzung zu Deiner show() Funktion, die ja sogar mit NULL als Argument umzugehen weiss.
    Du muesstest es halt dokumentieren:

    /* frees a complete list
       desc: frees all list elements and sets *l=NULL
       params: l pointer-to-pointer to a list
    */
    void clear_all(struct list **l);
    
    /* prints all list elements
       desc: prints all list elements. Does nothing if l is NULL.
       params: l pointer to a list or NULL
    */
    void show(struct list *l);
    

    Das ist jetzt fast idiotensicher - macht aber mehr Arbeit auf Deiner Seite und lädt Anwender zum schludern ein...

    Die Alternative:

    /* frees a complete list
       params: l pointer to the list to be freed
    */
    void clear_all(struct list *l);
    
    /* prints all list elements
     * params: l pointer to a list
     */
    void show(struct list *l);
    

    Da ist der Anwender selber schuld, wenn er das Interface falsch bedient, z.B. show() mit einem Zeiger aufruft, der gar nicht auf eine Liste zeigt.
    Fast alle Funktionen aus der C-Library sind so gestrickt, nochmal der Standard:

    J.2 Undefined behavior
    [...]
    — An argument to a library function has an invalid value or a type not expected by a function with variable number of arguments (7.1.4).

    .
    Evtl. ist auch eine Mischform gar nicht so schlecht.

    Wie dem auch sei: es ist doch nicht zu viel von einem Anwender verlangt darauf zu achten, dass er show() nur mit einem gültigen Zeiger aufruft, oder?



  • Wenn du dir mal überlegst, wann die while-Schleife beendet wird, wirst du merken, dass am Ende der Schleife list schon NULL ist.
    Daher ist diese neue Zuweisung überflüssig. Darum hat deine 2.Version mit dem daten=clear_all(daten); auch funktioniert.

    NULL steht für die Adresse 0. Da an der Adresse 0 meist irgendwelche systemspezifischen* Dinge gespeichert sind, wirst du mit einem normalen Programm darauf nicht zugreifen (können).
    Daher kann man NULL als Indikator für ungültige Adresse nehmen.

    Wenn du in deinem Programm aber erst Speicher freigibst und danach noch auf diesen Speicher zugreifen willst, stimmt mit deiner Programmlogik etwas nicht.

    *ROM, Resetvektoren, IO-Bausteine oder ähnliches.



  • Huh? Manchmal könntet Ihr Kerls Euch wirklich mehr Mühe beim formulieren geben...

    Also dafür echt mal ein ganz dickes ENTSCHULDIGUNG. Mir fällt das schon beim schreiben oft auf.... Manchmal wäre "echtes" unterhalten bedeutend einfacher.
    Naja, und es schießt oft schon ziemlich schnell aus mir raus, was nicht immer sooo überlegt ist

    Back to Topic:

    Wenn du in deinem Programm aber erst Speicher freigibst und danach noch auf diesen Speicher zugreifen willst, stimmt mit deiner Programmlogik etwas nicht.

    Ja, das ist mir mittlerweile klar. Aber...

    /* frees a complete list
       desc: frees all list elements and sets *l=NULL
       params: l pointer-to-pointer to a list
    */
    void clear_all(struct list **l);
    
    /* prints all list elements
       desc: prints all list elements. Does nothing if l is NULL.
       params: l pointer to a list or NULL
    */
    void show(struct list *l);
    

    ist mir jetzt noch nicht ganz klar. Ok clear_all bekommt dann einen Zeiger auf einen Zeiger der auf meine Liste zeigt... Dadurch könnte ich es so machen, das die Liste free wird, aber der "eine" Zeiger (Quasi der "Urzeiger" von *l der dann auch nicht mehr verkettet ist) speicherplatz behält , aber auf NULL zeigt??

    Hab ich das richtig verstanden?



  • beginner88888 schrieb:

    /* frees a complete list
       desc: frees all list elements and sets *l=NULL
       params: l pointer-to-pointer to a list
    */
    void clear_all(struct list **l);
    ...
    

    ist mir jetzt noch nicht ganz klar. Ok clear_all bekommt dann einen Zeiger auf einen Zeiger der auf meine Liste zeigt... Dadurch könnte ich es so machen, das die Liste free wird, aber der "eine" Zeiger (Quasi der "Urzeiger" von *l der dann auch nicht mehr verkettet ist) speicherplatz behält , aber auf NULL zeigt??

    Hab ich das richtig verstanden?

    Ich bin mir nicht sicher, was Du mit "Speicherplatz behält" meinst. Die Idee ist, der übergebene Zeiger wird auf NULL umgebogen.

    So wie Du es hier vorhast:

    beginner88888 schrieb:

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

    Wenn Du das so machst, kannst Du es auch gleich sein lassen - list ist ja eine Kopie des übergebenen Zeigers. Probier's aus.



  • void clear_all(Liste *list){  
      Liste *tmp;
      while(list){
        tmp=list->next;
        free(list);
        puts("del");
        list=tmp;
      }
      list=NULL;   // <=fällt unter Schnellschuß. Käse. 
    }
    

    Ja, das ist mittlerweile klar.

    Ich bin mir nicht sicher, was Du mit "Speicherplatz behält" meinst. Die Idee ist, der übergebene Zeiger wird auf NULL umgebogen.

    Ich glaub ich hab das so gemeint wie du es vorhattest, nur konnte ich es nicht richtig schreiben 🙂

    Mit *list hab ich nur eine Kopie des Zeigers, sprich ich kann die Liste Free machen, weil ich eine Kopie der Listenadressen hab, aber der Ursprüngliche Zeiger bleibt.

    Mit **list, mach ich mit dem Free das gleiche, setzte aber am Ende *list auf NULL.

    Also ich glaub das passt schon so. ABER...

    Die Idee ist, der übergebene Zeiger wird auf NULL umgebogen.

    nach clear_all(**list) ist *list=NULL.... Ich kann aber *list in show nur deswegen benuten, bzw. bekomme kein undefinertes verhalten, weil *list NICHT "gefreed" wurde sondern "nur" NULL zeigt ??



  • beginner88888 schrieb:

    Die Idee ist, der übergebene Zeiger wird auf NULL umgebogen.

    nach clear_all(**list) ist *list=NULL.... Ich kann aber *list in show nur deswegen benuten, bzw. bekomme kein undefinertes verhalten, weil *list NICHT "gefreed" wurde sondern "nur" NULL zeigt ??

    Nein, Du bekommst kein UB, weil Du nirgendwo einen NULL Zeiger oder einen Zeiger auf dem Du free() aufgerufen hast dereferenzierst.

    Der Ablauf wäre doch so:

    1. irgendwo in clear_all():
         free(*list); - klar der Speicherplatz der gesamten Liste muss freigegeben werden.
         *list = NULL;
    2. wenn show() jetzt souverän damit umgeht NULL als Argument zu bekommen -> kein Problem
    

    Trotzdem würde ich folgendes als schlechten Stil betrachten:

    Liste* l;
    // mit l arbeiten
    // ...
    clear_all(&l);
    show(l); // Logik?! Das ist einfach doof! ich habe l doch gerade gelöscht!
    

    Freigeben und auf NULL setzen sind zwei komplett unterschiedliche Dinge. Bei dieser Technik kommt nur beides zusammen.



  • wenn show() jetzt souverän damit umgeht NULL als Argument zu bekommen -> kein Problem

    Ist trotzdem doof, auch wenn´s kein Problem ist.
    Ich dachte ich bekomme UB schon allein deswegen, wenn ich einen Zeiger den ich gerade gefreed habe an eine Funktion übergebe, nicht erst wenn ich ihn dann in der Funktion dereferenziere...

    Nun gut, prinzipell hab ich's aber kapiert. Danke für die gute Erklärung, auch im anderen Thread...



  • So sieht jetzt die einfügen Version aus :

    Liste *einf_adv(Liste *list, Liste *insert, Liste *neu){    
          // *list =>Zeiger auf meine Liste ANFANG!! , *insert=>dahinter wird eingefügt, *neu =>daten des neuen Elements  RETURN : ZEiger auf Liste evtl. neuer ANFANG!!!!
    
    Liste *tmp, *anf;
    
    if (insert == list){            
               tmp=list;         
               list=neu;       
              // printf(" %c :: %d", list->cval, list->ival);
               list->next=tmp;   
               return list;     
               }
    
    anf=list;
    
    tmp=insert->next;   
    insert->next=neu;     
    neu->next=tmp;  
    
    return anf;
    
    }//END
    

    Ich denke das sollte so passen?? Glaub ich hab's jetzt "raus"... (Wenn das mal gut geht 😉 ... )


Anmelden zum Antworten