Verkettete Liste

Tutorials.at
Verfügbare Informationen zu "Verkettete Liste"

  • Qualität des Beitrags: 0 Sterne
  • Beteiligte Poster: detewe89 - wirthi - Dirty Oerty - Xin - DrPhil_Guth
  • Forum: Tutorials.at
  • Forenbeschreibung: Programmierforum
  • aus dem Unterforum: C / C++
  • Antworten: 21
  • Forum gestartet am: Mittwoch 19.04.2006
  • Sprache: deutsch
  • Link zum Originaltopic: Verkettete Liste
  • Letzte Antwort: vor 16 Jahren, 1 Monat, 26 Tagen, 22 Stunden, 9 Minuten
  • Alle Beiträge und Antworten zu "Verkettete Liste"

    Re: Verkettete Liste

    detewe89 - 22.12.2007, 13:14

    Verkettete Liste
    Hallo, ich hab grad versucht, meine erste verkettete Liste zu erstellen, gleich hat's nicht geklappt.

    Erst mal das Prog:

    Code: #include <stdio.h>
    #include <string.h>

    struct Person
    {
       char name[100],
            vorname[100],
            strasse[100],
            ort[100];
       int  hausnr,
            plz;
       struct Person *next;
    };

    void new_element(struct Person * _person, FILE *file)
    {
       struct Person _New;
       
       //find last element of list
       while (_person->next != NULL)
       {
          _person = _person->next;
       }
       
       //makes new element
       _person->next = (struct Person*)malloc(sizeof(struct Person));
       
       //reads data
       printf("Name? ");
       scanf("%s", _New.name);
       printf("Vorname? ");
       scanf("%s", _New.vorname);
       printf("Strasse? ");
       scanf("%s", _New.strasse);
       printf("Hausnummer? ");
       scanf("%d", &_New.hausnr);
       printf("Ort? ");
       scanf("%s", _New.ort);
       printf("PLZ? ");
       scanf("%d", &_New.plz);
       
       //copies data into new element
       strncpy(_person->name, _New.name, 100);
       strncpy(_person->vorname, _New.vorname, 100);
       strncpy(_person->strasse, _New.strasse, 100);
       _person->hausnr = _New.hausnr;
       strncpy(_person->ort, _New.ort, 100);
       _person->plz = _New.plz;
       
       //writes data to file
       fwrite(_person, sizeof(_person), 1, file);
    }


    int main(void)
    {
       struct Person * _person;
       int dec;
       FILE *file;
       
       file = fopen("testdaten.txt", "w");
       
       printf("Do you want to create a new list element?\n");
       printf("1\tYES\n2\tNO\n");
       scanf("%d", &dec);
       
       if (dec == 1)
       {
          new_element(_person, file);
       }
       
       return 0;
    }

    Der Fehler muss irgendwo beim malloc in der Funktion new_element liegen, der GDB hat folgendes ausgegeben:

    Code: (gdb) run
    Starting program: /home/daniel/list
    Do you want to create a new list element?
    1       YES
    2       NO
    1

    Program received signal SIGSEGV, Segmentation fault.
    0x08048564 in new_element (_person=0x64000000, file=0x804a008)
        at inf/c/071221struct.c:20
    20              while (_person->next != NULL)
    (gdb)


    Würde mich über Hilfe freuen!
    Daniel



    Re: Verkettete Liste

    wirthi - 22.12.2007, 15:57


    Ich hab's jetzt noch nicht durch den compiler geschmissen, zwei Sachen sind mir aufgefallen: zum einen solltest du die Variable _person bei main mit "NULL" initialisieren. Du nimmst ja an, dass sie zu beginn den Wert NULL hat denke ich. Das musst du aber auch in der Funktion new_element prüfen; _person->next geht da nicht, weil ja _person zu Beginn NULL sein wird; du musst also zusätzlich prüfen, ob nicht _person bereits NULL ist.

    Außerdem wirst du die Variable _New nicht brauchen; du kannst die Werte direkt in _person->next.name etc. einlesen.



    Re: Verkettete Liste

    Dirty Oerty - 22.12.2007, 17:28


    Du solltest vorallem noch einen Pointer auf den Anfange deiner Liste legen.
    SO verlierst du alle Einträge vor dem letzten.
    Auf die Weise tust du nix weiter als Speicher zumüllen :wink:



    Re: Verkettete Liste

    Xin - 22.12.2007, 18:14


    Da hakt noch mehr als das bisher genannte, aber soweit kommt das Programm nicht, weil es schon vorher abschmiert.

    Um das Problem zu lösen, rate ich davon ab die Person zu suchen, deren Next-Zeiger auf Null zeigt. Dafür müsste es erstmal eine Person geben...

    Such die Adresse des Zeiger, der auf Person zeigt (Person ** parameter).
    Wenn Du nun ein (Person *) suchst, dereferenzierst Du: (*parameter).
    parameter enthält also dann die Adresse von einem Zeiger (Person *), der null ist. Also schreibst Du auf den Zeiger (Person *), auf den Parameter (Person **) zeigt, die neue Person:
    *parameter = (struct Person *) malloc( sizeof( struct Person ) );

    Also änderst Du auch die Funktion:
    void new_element(struct Person ** _person, FILE *file)

    und übergibst in main die Adresse auf die mit NULL initialisierte Variable:

    struct Person * _person = NULL;
    new_element( &_person, file);

    Wenn Dir das wie ein Schüttelreim vorkommt... normal. Programmier es halt, dann passt das schon.


    Bitte gewöhne Dir die Unterstriche ab. Die sind zum einen scheiße zu lesen und zum anderen sind Symbole mit einleitenden Unterstrichen dem Compiler vorenthalten, wie _vtable oder __FILE__.



    Re: Verkettete Liste

    detewe89 - 24.12.2007, 14:03


    Sorry, aber mit Schüttelreimen komm' ich nicht wirklich klar, solche Dinge muss ich schon verstehen, sonst bringt mir das gar nichts.

    Also: In main() hab ich doch ein struct Person *_person deklariert. Folglich müssten dann doch schon die Variablen _person->name, _person->next und so weiter schon existieren. Und auf _person->next müsste dann doch (am Anfang) der Wert NULL liegen (-> es zeigt ja noch auf nichts)..
    Also versteh ich nicht, wieso das Programm nicht tut und warum ich mit Zeigern auf Zeiger arbeiten muss.

    Übrigens: Wie müsste meine Anweisung zum Finden des Listenendes lauten, wenn ich mit der Adresse von *_person arbeite???


    Daniel



    Re: Verkettete Liste

    DrPhil_Guth - 24.12.2007, 14:44


    Also dein Problem ist ganz einfach.
    Was du übergibst ist ein ZEIGER. Dann greifst du in deiner Funktion mithilfe dieses zeigers auf die Elemente der Struktur zu, auf die der Zeiger zeigt.

    Tja, dein Zeiger zeigt aber noch auf gar nichts!

    Code: int* int_ptr;
    *int_ptr = 3;

    Resultiert in einem Segmentation Fault. Wieso? es ist noch kein speicher für die variable reserviert, und der zeiger zeigt NIRGENDWO hin. Und ins nirgendwo zu schreiben kann wohl nicht funktionieren oder?

    Tja, das gleiche ist hier bei dir der Fall.

    wenn du sagst _person->next, dann wird auf den speicher zugegriffen, auf den _person zeigt. _person zeigt aber ins nirgendwo.

    Ich würde new_element erstmal nur so implementieren, dass es einfach nur den speicher reserviert und dann den zeiger zurückgibt. Deine funktion wäre erstmal für sowas gut wie "add_element", das ans ende deiner Liste einträge hinzufügt.



    Re: Verkettete Liste

    Xin - 24.12.2007, 15:18


    detewe89 hat folgendes geschrieben: Sorry, aber mit Schüttelreimen komm' ich nicht wirklich klar, solche Dinge muss ich schon verstehen, sonst bringt mir das gar nichts.
    Normal... programmiere es, dann verstehst Du es. ^^

    detewe89 hat folgendes geschrieben: Also: In main() hab ich doch ein struct Person *_person deklariert. Folglich müssten dann doch schon die Variablen _person->name, _person->next
    Nopes. Erklärung siehe Dr. Phil.
    Stell Dir vor, Du hast eine URL. www.german-bash.org. Coole URL? Langweilige URL... was Dich interessiert ist der Content. Wenn Du Dir jetzt german-bash.de registrierst, ist da kein Content hinter. Die URL ist nur der Zeiger, der sagt, wo der Content liegt, aber nicht, dass da Content wäre.

    detewe89 hat folgendes geschrieben: Also versteh ich nicht, wieso das Programm nicht tut und warum ich mit Zeigern auf Zeiger arbeiten muss.
    Weil Du erst content erschaffen musst. Das machst Du mit malloc. Und anschließend musst Du die Adresse Deines Contents (Person *) dahinschreiben, wo Du eine Adresse hinschreiben darfst. Also übergibst Du Deiner Funktion die Adresse der Adresse der Person (Person ** parameter) und speicherst dort, wo die Adresse hinzeigt (*parameter), die Adresse von Deiner Person: *parameter = ((struct Person *) malloc( sizeof( struct Person ));

    detewe89 hat folgendes geschrieben: Übrigens: Wie müsste meine Anweisung zum Finden des Listenendes lauten, wenn ich mit der Adresse von *_person arbeite???

    Etwa so:
    Code:
    void add( Person ** person )
    {
      while( *person ) person = &(*person)->next;
      *person = malloc();
    }



    Re: Verkettete Liste

    detewe89 - 25.12.2007, 17:21


    Ich hab's jetzt nochmal auf eine mir verständliche Art und Weise probiert, jetzt geht's auch, hakt jedoch weiter unten.
    Hier der neue Code:

    Code: #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>

    struct Person
    {
       char name[100],
            vorname[100],
            strasse[100],
            ort[100];
       int  hausnr,
            plz;
       struct Person * next;
    };

    void new_element(struct Person *_person, FILE * file)
    {
       struct Person _New;
       
       //finds last element of list and creates new element
       while (_person->next != NULL)
       {
          _person = _person->next;
       }
       _person->next = (struct Person*)malloc(sizeof(struct Person*));
       
       //reads data
       printf("Name? ");
       scanf("%s", _New.name);
       printf("Vorname? ");
       scanf("%s", _New.vorname);
       printf("Strasse? ");
       scanf("%s", _New.strasse);
       printf("Hausnummer? ");
       scanf("%d", &_New.hausnr);
       printf("Ort? ");
       scanf("%s", _New.ort);
       printf("PLZ? ");
       scanf("%d", &_New.plz);
       
       //copies data into new element
       strncpy(_person->name, _New.name, 100);
       strncpy(_person->vorname, _New.vorname, 100);
       strncpy(_person->strasse, _New.strasse, 100);
       _person->hausnr = _New.hausnr;
       strncpy(_person->ort, _New.ort, 100);
       _person->plz = _New.plz;
       
       //prints out test data --DEBUG--
       printf("%s\n", _person->strasse);
       
       //writes data to file
       fwrite(_person, sizeof(struct Person), 1, file);
    }

    int main(void)
    {
       struct Person * _person;
       int dec;
       FILE *file;
       
       _person = (struct Person*)malloc(sizeof(struct Person*));
       printf("%p\n", _person);
       file = fopen("testdaten.txt", "w");
       
       printf("Do you want to create a new list element?\n");
       printf("1\tYES\n2\tNO\n");
       scanf("%d", &dec);
       
       if (dec == 1)
       {
          new_element(_person, file);
       }
       
       fclose(file);
       
       return 0;
    }

    Der GDB findet den Fehler jetzt in fwrite():

    Code: (gdb) run
    Starting program: /home/daniel/list
    0x804a008
    Do you want to create a new list element?
    1       YES
    2       NO
    1
    Name? Test
    Vorname? Rudolf
    Strasse? ImHolz
    Hausnummer? 1
    Ort? Musterdorf
    PLZ? 123 
    ImHolz

    Program received signal SIGSEGV, Segmentation fault.
    0xb7eafb37 in fwrite () from /lib/tls/i686/cmov/libc.so.6
    (gdb)


    Die Parameter hab ich doch richtig angegeben, oder?... :cry:


    Danke!
    detewe89



    Re: Verkettete Liste

    Xin - 26.12.2007, 00:09


    detewe89 hat folgendes geschrieben: Ich hab's jetzt nochmal auf eine mir verständliche Art und Weise probiert, jetzt geht's auch, hakt jedoch weiter unten.

    Hatte ich ja schon angekündigt, und es hakt gewaltig. Um Dein Programm lauffähig zu bekommen, musste ich auch mehrfach hingucken.

    Code:
    void new_element(struct Person *_person, FILE * file)
    {
       struct Person _New;
       
       //finds last element of list and creates new element
       while (_person->next != NULL)

    Du hast _person->next nie initialisiert. Vielleicht klappts, wahrscheinlich nicht....
    Außerdem... _person ist doch noch unbeschrieben, wozu also eine neue anlegen?

    Code:
       ...
       //copies data into new element
       strncpy(_person->name, _New.name, 100);
       ...
    }

    Wozu soll das kopieren gut sein?

    Code:
    int main(void)
    {
       struct Person * _person;
       int dec;
       FILE *file;
       
       _person = (struct Person*)malloc(sizeof(struct Person*));

    <hust>
    Das hier ist ein Widerspruch in sich, wenn Du castest, dann bitteschön mit bedacht.

    Es ist ein typischer Fehler - gewöhn Dir malloc nicht an, sondern benutze gleich new und delete von C++. Du musst den Compiler dann mit g++ rufen, statt gcc, ansonsten ändert sich nix.
    Edit: also es heißt dann _person = new Person(). Der Aufruf von new ist schon anders als von malloc... ich meinte im restlichen Programm ändert sich nix.

    Ansonsten gibt man Speicher auch wieder frei, wenn man ihn nicht mehr benötigt.

    detewe89 hat folgendes geschrieben:
    Der GDB findet den Fehler jetzt in fwrite():

    Code:
    Program received signal SIGSEGV, Segmentation fault.
    0xb7eafb37 in fwrite () from /lib/tls/i686/cmov/libc.so.6
    (gdb)

    Kompiliere mit dem Schalter -g (gcc/g++ -g programm.c), dann kann Dir der gdb Zeilennummer und Datei angeben, wo das Programm abgeschmiert ist. Mit "backtrace" erhältst Du einen Stacktrace, also welche Funktion hat welche aufgerufen.

    detewe89 hat folgendes geschrieben:
    Die Parameter hab ich doch richtig angegeben, oder?... :cry:

    Es hat nichts mit dem zu tun, was ich Dir vorgeschlagen habe. Ich würde weiterhin Zeiger auf Zeiger auf Person übergeben.

    Und bitte nimm den Unterstrich aus den Variablen. Der Unterstrich vor den Variablen soll dem Compilerhersteller vorbehalten bleiben.



    Re: Verkettete Liste

    detewe89 - 30.12.2007, 19:56


    So, das mit dem neuen Daten erstellen hat geklappt... jetzt hakt das Programm bei der Einlesefunktion:

    Code: void loaddata(struct Person ** person, FILE * file)
    {
       printf("Loading data from file...");
       
       if (!EOF)
       {
          *person = (struct Person*)malloc(sizeof (struct Person));
       
          while (!EOF)
          {
             fread(*person, sizeof(struct Person), 1, file);
             (*person)->next = (struct Person*)malloc(sizeof(struct Person));
             if (!EOF)
             {
                *person = (*person)->next;
             }
          }
          (*person)->next = NULL;
          printf("from loaddata(): %d\n", (*person)->plz); //DEBUG
       }   
       
       printf(" Done\n");
       printf("person = %p\n", person); //DEBUG
       printf("*person = %p\n", *person); //DEBUG
    }

    Woran liegt das denn - ich hab' in der Testdatei übrigens mehrere Datensätze!

    Daniel



    Re: Verkettete Liste

    Xin - 30.12.2007, 20:17


    detewe89 hat folgendes geschrieben: So, das mit dem neuen Daten erstellen hat geklappt... jetzt hakt das Programm bei der Einlesefunktion:

    FAQ: "HILFE ES FUNKTIONIERT NICHT"

    Dein Quelltext enthält Redundanzen. Redundanzen sind schwer zu pflegen, also fehleranfällig, was Dein Quelltext vorbildlich zeigt. Einmal fragst du erst, ob Du eine Person anlegen musst, dann tust es einfach und fragst danach, ob das überhaupt nötig war.

    Ich habe keine Ahnung, was an Deinem Code sonst noch verkehrt sein könnte, da ich weder weiß, wie Du die Funktion rufst, noch was überhaupt fehlschlägt.

    EDIT: Sag mal speicherst Du die Next-Zeiger mit ab...!??? Das lässt Dein Programm zwar nicht abschmieren, aber meine Fußnägel rollen sich...



    Re: Verkettete Liste

    DrPhil_Guth - 31.12.2007, 20:44


    detewe89 hat folgendes geschrieben: So, das mit dem neuen Daten erstellen hat geklappt... jetzt hakt das Programm bei der Einlesefunktion:

    Code: void loaddata(struct Person ** person, FILE * file)
    {
       printf("Loading data from file...");
       
       if (!EOF)
       {
          *person = (struct Person*)malloc(sizeof (struct Person));
       
          while (!EOF)
          {
             fread(*person, sizeof(struct Person), 1, file);
             (*person)->next = (struct Person*)malloc(sizeof(struct Person));
             if (!EOF)
             {
                *person = (*person)->next;
             }
          }
          (*person)->next = NULL;
          printf("from loaddata(): %d\n", (*person)->plz); //DEBUG
       }   
       
       printf(" Done\n");
       printf("person = %p\n", person); //DEBUG
       printf("*person = %p\n", *person); //DEBUG
    }

    Woran liegt das denn - ich hab' in der Testdatei übrigens mehrere Datensätze!

    Daniel

    So also als allererstes will ich dich bitten, dass du mal lernst deine Progs selber zu debuggen, dafür haben wir in der FAQ nen eintrag:
    Wie debugge ich richtig
    Sieh dir nochmal dein Programm an, denk nach was es tun soll, was du erreichen willst und wie du es implementierst. Dann vergleiche mit dem was du geschrieben hast...
    Kurzum denk noch alles durch.

    Ein beispiel:


    Code: if (!EOF)
    {
    ...
    }

    :shock: WTF???
    Das macht mich echt stutzig... Das sagt leider einiges darüber aus wie sehr du dich mit dem Problem beschäftigt hast, nämlich gar nicht.
    Ein kleiner Blick hier rein hätte dir gezeigt dass EOF einen _fixen_ Wert hat, der höchstwahrscheinlich nicht 0 ist. Das bedeutet der Code wird NIE ausgeführt.
    Sieh mal, darauf wärst du spätestens dann gekommen, wenn du mit ein paar einfachen printf()'s überprüft hättest welcher Code ausgeführt wird oder nicht. Aber das hast du anscheinend nicht.

    Also wie gesagt, nochmal nachdenken was du eigentlich willst, dann nochmal schreiben, und nicht vergessen bei jeder bibliothek (auch stdio!!!) nachzusehen ob du sie auch richtig verwendest.



    Re: Verkettete Liste

    Xin - 01.01.2008, 15:59


    DrPhil_Guth hat folgendes geschrieben: Code: if (!EOF)
    {
    ...
    }

    :shock: WTF???
    Das macht mich echt stutzig...
    Ein kleiner Blick hier rein hätte dir gezeigt dass EOF einen _fixen_ Wert hat, der höchstwahrscheinlich nicht 0 ist. Das bedeutet der Code wird NIE ausgeführt.
    Treffer ^^
    Irgendwie kam mir EOF mangels Klammern für einen Funktionsaufruf auch komisch vor, aber es sah so ähnlich wie feof() aus, dass es wohl nicht laut genug klingelte.

    Um Deine Quelle zu zitieren: "It expands to a negative integral constant expression.", was tatsächlich höchstwahrscheinlich nicht 0 ist.

    DrPhil_Guth hat folgendes geschrieben: Sieh mal, darauf wärst du spätestens dann gekommen, wenn du mit ein paar einfachen printf()'s überprüft hättest welcher Code ausgeführt wird oder nicht. Aber das hast du anscheinend nicht.
    Macht etwas den Eindruck.

    Jetzt wo Du ihm deutlich verständlich gemacht hast, dass es nicht Dein (oder mein) Job ist, seinen Programmfluss zu kontrollieren, wird er sicherlich in Zukunft erst ein paar printf() in den Code setzen, um zu sehen, wie sich das Programm denn so verhält. Damit kann er ggfs. seine Fragen schon selbst beantworten. Damit hast Du ihm zu einer neuen Erkenntnis verholfen und dafür ist so ein Forum ja da. ^^

    Also, detewe... printf ist bester Debugger, wo geben tut.
    Benutz ihn.

    Dann kannst Du bei komplizierteren Fragen demnächst auch ein paar Fehlerausgaben mitlieferen. ^^

    Ansonsten... frohes Neues.



    Re: Verkettete Liste

    detewe89 - 17.03.2008, 21:45


    So, da bin ich wieder :-) Hab jetzt fast ein halbes Jahr nix mehr programmiert, und das erste, was ich jetzt gemacht hab, ist natürlich mein Listen-Programm fertigzustellen! Und es tut auch endlich!

    Das Problem war Folgendes: In der Funktion readset() hatte ich den Schritt
    Code: start = *person;
    und
    Code: *person = start;
    jeweils am Anfang und am Ende der Funktion ausgelassen, sodass die ganze Liste natürlich jedes Mal erneut überschrieben wurde!!!
    Hier nun das Programm (es kann noch keine Daten ausgeben!):

    Code: /*080315struct.c

     - Program to demonstrate the use and implementation of chained lists in C
     - works only under unixlike systems

    */

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    // G L O B A L   V A R S
    struct Person
    {
      char name[30],
        first_name[50];
      struct Person *next;
    };

    // F U N C T I O N   P R O T O T Y P E S
    void readset(struct Person **);
    void write_to_file(struct Person **, char *filepath);

    //////////////////////////////////////////////////

    int main(void)
    {
       //FILE *file = NULL;
       int dec=1; //user interface var
       struct Person *person = 0; //initialization of chained list
       char filepath[100]; //name of file where to write the data

       printf("New list element?\n0\tNO\n1\tYES\n");
       scanf("%d%*c", &dec);

       while (dec == 1)
       {
          readset(&person);
          printf("New list element?");
          scanf("%d%*c", &dec);
       }

       if (dec == 0)
       {
          printf("Where do you want to write the data?\n./");
          gets(filepath);
       }
       
       write_to_file(&person, filepath);
       return 0;
    }

    //////////////////////////////////////////////////

    //reads from stdin (user input) - new list element/dataset
    void readset(struct Person **person)
    {
       if (*person == 0)
       {
          *person = (struct Person *)malloc(sizeof(struct Person));
          
          printf("Name? ");
          gets((*person)->name);
          printf("First name? ");
          gets((*person)->first_name);
          
          (*person)->next = 0;
       }

       else
       {
          struct Person *start; //to save the beginning's address of chained list
          start = *person;
          while ((*person)->next != 0) //finds last list element
          {
             *person = (*person)->next;
          }

          (*person)->next = (struct Person *)malloc(sizeof(struct Person));
          *person = (*person)->next;

          printf("N a m e ? ");
          gets((*person)->name);
          printf("F i r s t   n a m e? ");
          gets((*person)->first_name);

          (*person)->next = 0;
          *person = start;
       }
    }

    //////////////////////////////////////////////////

    //writes all data to file before program stop
    void write_to_file(struct Person **person, char *filepath)
    {
       char directory[100] = "./";

       strcat(directory, filepath);
       FILE *file = fopen(directory, "w");
       
       if (*person != 0)
       {
          fwrite(*person, sizeof(struct Person), 1, file);
          fclose(file);
          
          while ((*person)->next != 0)
          {
             file = fopen(directory, "a");
             *person = (*person)->next;
             fwrite(*person, sizeof(struct Person), 1, file);
             fclose(file);
          }
       }
    }
    Vielleicht hilft's ja mal dem einen oder anderen.

    Eine Frage hätte ich noch: Am Codeende muss ich die Datei ständig mit fopen(..., "a"); öffnen und wieder schließen, damit nichts überschrieben wird - kann ich das auch vereinfachen?


    Liebe Grüße
    detewe89



    Re: Verkettete Liste

    Xin - 18.03.2008, 00:20


    detewe89 hat folgendes geschrieben: So, da bin ich wieder :-) Hab jetzt fast ein halbes Jahr nix mehr programmiert,
    Naja, in meiner Zeitrechnung sind's nichtmals drei Monate.

    detewe89 hat folgendes geschrieben:
    Code: /*080315struct.c

     - Program to demonstrate the use and implementation of chained lists in C
     - works only under unixlike systems

    */

    /* XIN: Why does it work only with windows systems? */

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    // G L O B A L   V A R S
    struct Person
    {
      char name[30],
        first_name[50];
      struct Person *next;
    };

    // F U N C T I O N   P R O T O T Y P E S
    void readset(struct Person **);
    void write_to_file(struct Person **, char *filepath);

    //////////////////////////////////////////////////

    int main(void)
    {
       //FILE *file = NULL;
       int dec=1; //user interface var
       struct Person *person = 0; //initialization of chained list

    /* XIN: if you use a pointer it's a good idea to use NULL instead of 0, because we all know that a pointer is an integer internally but we do not write assembler programs, don't we?
    Additionally: I recommend to use '\0' to sign a 0 for a character instead of 0.
    */

       char filepath[100]; //name of file where to write the data

       printf("New list element?\n0\tNO\n1\tYES\n");
       scanf("%d%*c", &dec);

    /* XIN: Here I recommend to use parameters instead of the dangerous function scanf() - it is also easier to handle */

       while (dec == 1)
       {
          readset(&person);
          printf("New list element?");
          scanf("%d%*c", &dec);
       }

    /* XIN: Try do {} while(); to prevent redundancy */

       if (dec == 0)
       {
          printf("Where do you want to write the data?\n./");
          gets(filepath);
       }
       
       write_to_file(&person, filepath);
       return 0;
    }

    //////////////////////////////////////////////////

    //reads from stdin (user input) - new list element/dataset
    void readset(struct Person **person)
    {
       if (*person == 0)
       {
          *person = (struct Person *)malloc(sizeof(struct Person));
          
          printf("Name? ");
          gets((*person)->name);
          printf("First name? ");
          gets((*person)->first_name);
          
          (*person)->next = 0;
       }

       else
       {
          struct Person *start; //to save the beginning's address of chained list
          start = *person;
          while ((*person)->next != 0) //finds last list element
          {
             *person = (*person)->next;
          }

          (*person)->next = (struct Person *)malloc(sizeof(struct Person));
          *person = (*person)->next;

          printf("N a m e ? ");
          gets((*person)->name);
          printf("F i r s t   n a m e? ");
          gets((*person)->first_name);

    /* XIN: Why do you do your stuff twice? Same questions as before - use a function! Always prevent redundancy! */

          (*person)->next = 0;
          *person = start;
       }

    /* XIN: This is a typical beginner function. ^^ Twice the code as needed.
    Why don't you delete the if-condition and the then-part?
    No redundancy and less code - same functionality. */

    }

    //////////////////////////////////////////////////

    //writes all data to file before program stop
    void write_to_file(struct Person **person, char *filepath)
    {
       char directory[100] = "./";

       strcat(directory, filepath);
       FILE *file = fopen(directory, "w");

       if (*person != 0)
       {
          fwrite(*person, sizeof(struct Person), 1, file);
          fclose(file);
          
          while ((*person)->next != 0)
          {
             file = fopen(directory, "a");
             *person = (*person)->next;
             fwrite(*person, sizeof(struct Person), 1, file);
             fclose(file);
          }
       }
    }
    Vielleicht hilft's ja mal dem einen oder anderen.

    Eine Frage hätte ich noch: Am Codeende muss ich die Datei ständig mit fopen(..., "a"); öffnen und wieder schließen, damit nichts überschrieben wird - kann ich das auch vereinfachen?
    Yepp, lass es. Warum sollte etwas überschrieben werden?
    Ansonsten... heißt es nicht "w+"? Ich vergess das immer...



    Re: Verkettete Liste

    DrPhil_Guth - 18.03.2008, 03:17


    Hi! Dein Programm schaut ja eigentlich ganz sauber aus. Allerdings gibts neben den von Xin erwähnten dingen noch etwas, und zwar deine vielen schönen gets().
    detewe89 hat folgendes geschrieben: Code:       printf("Where do you want to write the data?\n./");
          gets(filepath);
    Dieses hier ist mein besonderer liebling. Wieso? Nun, dein Puffer ist 100 zeichen groß (was mich ein bisschen wundert, denn normalerweise kann ein unix- filename bis zu 255 zeichen lang sein.) Was ist wenn der benutzer mehr als 100 zeichen eingibt? tja, es gibt nen Buffer overflow. Das führt im besten fall zu seltsame verhalten oder nem Segfault. Im schlimmsten fall kann man damit beliebigen code ausführen. Ich würde dir das gerne demonstrieren, aber dafür bräuchte ich eine Kompilierte version des Programms. Vielleicht schaff ichs ja, dass sich da was machen lässt. (kann nix versprechen).
    Eine alternative wäre das hier:
    Code: fgets(filepath, 100, stdin);
    filepath[99] = '\0';
    Fakt ist, wäre das eine anwendung mit suid bit könnte man sich unbeschränkten (root) zugang zum system verschaffen. also bitte aufpassen mit benutzereingaben.
    Die anderen gets in deinem programm lassen sich zumindest für einen heap- overflow nutzen. (keine ahnung wie das geht)

    Des weiteren gäbe es hier noch das scanf, das in diesem fall zwar keine "sicherheitslücke" darstellt, sehr wohl allerdings bei falschen benutzereingaben das programm sich ganz und gar nicht verhält wie du es erwartest. sieh bitte hier nach, wieso: http://www.iphpbb.com/board/viewtopic.php?nxu=53411326nx34195&p=2756#2756
    Übrigens würde ich dir empfehlen mit scanf sehr vorsichtig zu sein, denn in einer anderen anwendung könnte es sehr wohl eine sicherheitslücke werden.

    Ach und noch was: dein programm sollte ohne probleme unter nicht- unix system laufen. mit einer kleinen ausnahme: das "./" bei pathname ist nicht portabel, aber das solltest du auch entfernen, denn es bringt nichts. Es heißt nichts anderes als "in diesem verzeichnis", und dort sieht jedes betriebssystem sowieso als erstes nach.

    ansonsten nicht so schlecht deine verkettete liste.

    ps.: könntest du mir bitte eine kompilierte version deines programms schicken? Ich würd gern ein bisschen üben. ;-)



    Re: Verkettete Liste

    detewe89 - 18.03.2008, 11:25


    Also, ich hab Dir jetzt mal die kompilierte Version und den erweiterten QT hochgeladen:

    EXE: http://rapidshare.com/files/100418028/list.html
    QT: http://rapidshare.com/files/100418497/080315struct.c.html

    mfg
    detewe89 :)



    Re: Verkettete Liste

    Xin - 18.03.2008, 11:45


    DrPhil_Guth hat folgendes geschrieben: Hi! Dein Programm schaut ja eigentlich ganz sauber aus. Allerdings gibts neben den von Xin erwähnten dingen noch etwas, und zwar deine vielen schönen gets().
    Ich habe die für mich mit scanf() abgehakt. Ich wollte das nun nicht unter jede Zeile schreiben.

    DrPhil_Guth hat folgendes geschrieben: detewe89 hat folgendes geschrieben: Code:       printf("Where do you want to write the data?\n./");
          gets(filepath);
    Dieses hier ist mein besonderer liebling. Wieso? Nun, dein Puffer ist 100 zeichen groß (was mich ein bisschen wundert, denn normalerweise kann ein unix- filename bis zu 255 zeichen lang sein.) Was ist wenn der benutzer mehr als 100 zeichen eingibt? tja, es gibt nen Buffer overflow. Das führt im besten fall zu seltsame verhalten oder nem Segfault. Im schlimmsten fall kann man damit beliebigen code ausführen. Ich würde dir das gerne demonstrieren, aber dafür bräuchte ich eine Kompilierte version des Programms. Vielleicht schaff ichs ja, dass sich da was machen lässt. (kann nix versprechen).
    Eine Anleitung findest Du im Buch "Forbidden Code" in den vorderen Kapiteln.
    Allerdings musst Du Maschinensprache in den String kodieren.

    DrPhil_Guth hat folgendes geschrieben: Eine alternative wäre das hier:
    Code: fgets(filepath, 100, stdin);
    filepath[99] = '\0';
    Was spricht gegen Parameter, dann kann man das Programm auch in Skripten aufrufen...?



    Re: Verkettete Liste

    DrPhil_Guth - 18.03.2008, 12:48


    @ detewe89:
    Danke! Ich versuch mal mein bestes, vielleicht kriegst in ein paar tagen ja deinen Trojaner (-:

    Xin hat folgendes geschrieben: DrPhil_Guth hat folgendes geschrieben: detewe89 hat folgendes geschrieben: Code:       printf("Where do you want to write the data?\n./");
          gets(filepath);
    Dieses hier ist mein besonderer ... [schnip]
    Eine Anleitung findest Du im Buch "Forbidden Code" in den vorderen Kapiteln.
    Allerdings musst Du Maschinensprache in den String kodieren.
    Danke für den Tipp. Aber ich empfehle das original: http://insecure.org/stf/smashstack.html
    Da hab ichs nähmlich gelernt. Man muss allerdings beachten dass das dokument schon älter ist, und ein paar dinge heute nicht anwendbar sind, beispielsweise kann man die startadresse des stacks nicht mehr erraten, weil sie ab irgend einer linux- kernel version bei jedem Programmstart zufällig gewählt wird. Aber keine sorge es gibt noch andere wege: http://www.tty64.org/doc/smackthestack.txt. Assembly kann ich schon und ich hab gestern endlich meinen ersten kleinen buffer overflow eploit geschrieben (Als "Schulbeispiel" eine funktion mit strcpy() )
    Was schwierig werden könnte ist den code einzuschleusen, denn gets() liest ja von der standardeingabe, und von der tastatur aus kann man ja wohl schlecht nicht druckbare zeichen eingeben oder? Ideen?
    Ich könnt ja versuchen alphanumerischen code zu schreiben, aber das wird schwierig...

    Xin hat folgendes geschrieben: DrPhil_Guth hat folgendes geschrieben: Eine alternative wäre das hier:
    Code: fgets(filepath, 100, stdin);
    filepath[99] = '\0';
    Was spricht gegen Parameter, dann kann man das Programm auch in Skripten aufrufen...?
    Nichts eigentlich, parameter find ich ziemlich gut. Allerdings meinte ich nicht die Wahl des Interfaces sondern eher die sicherheit wenn man wirklich den Benutzer etwas eintippen lassen will. mit gets() hat man keine chance zu bestimmen, wie lang die eingabe sein soll. Mit dieser variante schon.



    Re: Verkettete Liste

    detewe89 - 18.03.2008, 14:08


    Zitat: von der tastatur aus kann man ja wohl schlecht nicht druckbare zeichen eingeben oder?
    Geht das nicht mit Ctrl+... ?

    Zitat: Fakt ist, wäre das eine anwendung mit suid bit könnte man sich unbeschränkten (root) zugang zum system verschaffen. also bitte aufpassen mit benutzereingaben.
    Ok, auf die Sicherheit hab ich noch nicht geachtet, ehrlich gesagt hab ich mich da auch noch nie eingearbeitet. Aber ich werd mir mal eure Links ansehen, weil sicher proggen möchte ich ja schon... Bis jetzt ging's mir ja auch nur darum, dieses Programm irgendwie zum Laufen zu bringen; im Studium werd ich das ja sicher alles noch genauer mitbekommen...

    Naja, sonst danke für eure Empfehlungen, ich werd bald mal eine verbesserte Version hochladen :-)


    Gruß
    Daniel



    Re: Verkettete Liste

    Xin - 18.03.2008, 22:13


    DrPhil_Guth hat folgendes geschrieben: Xin hat folgendes geschrieben: Eine Anleitung findest Du im Buch "Forbidden Code" in den vorderen Kapiteln.
    Allerdings musst Du Maschinensprache in den String kodieren.
    Danke für den Tipp. Aber ich empfehle das original: http://insecure.org/stf/smashstack.html
    Da hab ichs nähmlich gelernt.
    Ich glaube, das "Original" wird keiner von uns je gesehen haben. ^^

    DrPhil_Guth hat folgendes geschrieben: Man muss allerdings beachten dass das dokument schon älter ist, und ein paar dinge heute nicht anwendbar sind, beispielsweise kann man die startadresse des stacks nicht mehr erraten, weil sie ab irgend einer linux- kernel version bei jedem Programmstart zufällig gewählt wird. Aber keine sorge es gibt noch andere wege:
    Ich war nie daran interessiert etwas kaputt zu machen. Das Buch habe ich, um Programme zu schreiben, die nicht kaputt gehen.

    DrPhil_Guth hat folgendes geschrieben: Was schwierig werden könnte ist den code einzuschleusen, denn gets() liest ja von der standardeingabe, und von der tastatur aus kann man ja wohl schlecht nicht druckbare zeichen eingeben oder? Ideen?
    Wie kommst Du darauf, dass die Standardeingabe eine besondere Eingabe wäre?
    Schreib ein Programm, dass das fehlerhafte Programm aufruft und sich selbst als Standardquelle und -ausgabe setzt.
    Wenn in Deiner Eingabe Datei Zeichen auftauchen parst Du sie und wenn die erwartete Abfrage kommt, haust Du Dein Programm da rein.
    Fertig.



    Mit folgendem Code, können Sie den Beitrag ganz bequem auf ihrer Homepage verlinken



    Weitere Beiträge aus dem Forum Tutorials.at

    Mache ich den richtigen Kurs? - gepostet von Cypher am Samstag 03.03.2007
    Übersetzter - gepostet von Bratwurst am Donnerstag 06.09.2007
    Bilder - gepostet von BASIC am Mittwoch 31.05.2006
    CS-Video ? - gepostet von exbs am Freitag 03.11.2006
    hab ein problem mit turbo C - gepostet von AMÖ27 am Sonntag 13.05.2007
    Wie bist Du auf diese Seite aufmerksam geworden? - gepostet von matze(2) am Samstag 15.07.2006
    spätere jobs - gepostet von CRASH am Donnerstag 23.11.2006



    Ähnliche Beiträge wie "Verkettete Liste"

    eigentlich lächerlich für schwarze liste-level1 - Ryak (Freitag 24.08.2007)
    Schwarze Liste - Anonymous (Sonntag 11.09.2005)
    Liste der freien Charas - cassy1610 (Sonntag 30.04.2006)
    Aion : The Tower of Eternity - Torque (Montag 15.06.2009)
    Liste - Netherwind-Verteilung - Volupta (Freitag 07.07.2006)
    Rote Liste Zusammensetzung - Friesin (Dienstag 21.08.2007)
    Liste der Serien - Ray (Mittwoch 20.09.2006)
    Schwache Heizung - Mercury (Sonntag 29.01.2012)
    icq liste - bomberpilot (Montag 14.08.2006)
    Gilden Crafter Liste - Woozy (Dienstag 13.11.2007)