Verkettete Liste



  • Da du das Ganze zur Übung machst, solltest du jetzt lieber einen cut machen und dir erstmal die Standardlibs anschauen. Es sind ja nicht mal viele Funktionen dort enthalten. Des weiteren ist dir wärmstens zu empfehlen den Umgang mit Array und Variablen zu verbessern. Danach schaue dir nochmals die Pointer an und warum Pointer und Arrays so ähnlich sind. Fange an einfache Programme mit steigendem Schwierigkeitsgrad zu programmieren und wenn du damit gut zurecht kommst, versuche dich an einer "Einfach verketteten Liste". Wenn diese klar und einfach für dich scheint, dann mache die doppelte.

    Nützlicher Link zu C-Libs mit Beispielen:
    http://www.cplusplus.com/reference/clibrary/

    Einrücken ist Geschmackssache aber dein Gebracuh und die etlichen Leerzeilen sind anstrengen 😉

    //KEINE FEHLERVERBESSERUNG NUR EINRÜCKEN
    #include <stdio.h> 
    #include <stdlib.h> 
    #include <string.h> 
    
    typedef struct Knot* Liste; 
    
    typedef struct Land
    { 
        char Name[100]; 
        double Einwohner; /*Einwohner in Millionen*/ 
    }Land; 
    
    typedef struct Knot 
    { 
        Land eintrag; 
        Liste vorgaenger; 
        Liste nachfolger; 
    }Knoten; 
    
    typedef struct 
    { 
        Liste anfang; 
        Liste ende; 
    }anfang_ende_liste; 
    
    void einfuegen(anfang_ende_liste* liste, char *c, double anzahl)
    {
    	if(liste->anfang == NULL)
    	{ 
    		Liste tmp = (Liste) malloc(1*sizeof(Knoten)); 
    		liste->anfang = tmp; 
    		liste->ende = tmp; 
    		liste->anfang->nachfolger = NULL; 
    		liste->anfang->vorgaenger = NULL; 
    		strcpy(tmp->eintrag.Name, c); 
    		tmp->eintrag.Einwohner = anzahl; 
    	}
        else
        {
            //....
        }
    } 
    
    int main()
    { 
    	char c[100]; 
    	int anzahl; 
    	anfang_ende_liste liste; 
    	char ende[]="ende"; 
    
    	do
    	{ 
    		printf("Geben sie Land und Bevoelkerungsgroeße ein\n"); 
    		scanf("%s %d",c,&anzahl); 
    
    		einfuegen(&liste,c,anzahl); 
    	} while(strcmp(ende,c) !=0); 
    
    	return 0; 
    }
    


  • Ich danke Dir nochmal vielmals für Deine Hilfe und Deine Ratschläge sind auch sicher absolut richtig. Aber ich habe nicht mehr so viel Zeit und ich muss jetzt einfachmal ein paar Eveolutionsstufen überspringen. Und heute war ja auch ein erfolgreicher Tag. Heute morgen wusste ich noch nicht was ne Liste ist und vorgestern habe ich im wesentlichen noch Programme geschrieben, die die Zahlen von 1 bis 10 addieren konnten, oder sowas 🙂 Ich bin jetzt gerade an der Ausgabe.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct Knot* Liste;
    
    typedef struct
        {
            char Name[30];
            double Einwohner; /*Einwohner in Millionen*/
    
        }Land;
    
    typedef struct Knot
        {
            Land eintrag;
            Liste vorgaenger;
            Liste nachfolger;
        }Knoten;
    
    typedef struct
        {
            Liste anfang;
            Liste ende;
        }anfang_ende_liste;
    
    void einfuegen(anfang_ende_liste* liste, char *c, double anzahl){
    
        if(liste->anfang==NULL){
            Liste tmp=(Liste) malloc(1*sizeof(Knoten));
            liste->anfang=tmp;
            liste->ende=tmp;
            liste->anfang->nachfolger=NULL;
            liste->anfang->vorgaenger=NULL;
            strcpy(tmp->eintrag.Name, c);
            tmp->eintrag.Einwohner=anzahl;
        }
    
        if (anzahl<100){
            Liste tmp=(Liste) malloc(1*sizeof(Knoten));
            strcpy(tmp->eintrag.Name, c);
            tmp->eintrag.Einwohner=anzahl;
            tmp->vorgaenger=NULL;
            tmp->nachfolger=liste->anfang;
            liste->anfang=tmp;
            tmp->nachfolger->vorgaenger=tmp;
        }
    
        if (anzahl>=100){
            Liste tmp=(Liste) malloc(1*sizeof(Knoten));
            strcpy(tmp->eintrag.Name, c);
            tmp->eintrag.Einwohner=anzahl;
            tmp->nachfolger=NULL;
            tmp->vorgaenger=liste->ende;
            liste->ende=tmp;
            tmp->vorgaenger->nachfolger=tmp;
    
        }
    
    }
    
    void ausgeben(anfang_ende_liste liste){
    
        Liste tmp=liste.anfang;
        while (tmp!=0){
            printf("%s %d", tmp->eintrag.Name, tmp->eintrag.Einwohner);
            tmp=tmp->nachfolger;
    
        }
    }
    
    int main(void){
    
    char c[30];
    int anzahl;
    anfang_ende_liste liste;
    char ende[]="ende";
    
    do{
    
        printf("Geben sie Land und Bevoelkerungsgroesse ein\n");
        scanf("%s %d",c,&anzahl);
    
        einfuegen(&liste,c,anzahl);
    
    }
    while(strcmp(ende,c) !=0);
    
    ausgeben(liste);
    
    return 0;
    }
    


  • psigh schrieb:

    .... Heute morgen wusste ich noch nicht was ne Liste ist und vorgestern habe ich im wesentlichen noch Programme geschrieben, die die Zahlen von 1 bis 10 addieren konnten, oder sowas 🙂 ...

    Da hast du dann aber das ganz wichtige Kapitel Felder und Zeiger (Arrays/Pointer) übersprungen. Zumindest nicht verstanden und vertieft.

    Wiederhole das Thema nochmal!
    Dann kannst du dich an dynamische Speicherverwaltung und Listen wagen.



  • Ok. Ich werds sowieso machen müssen. Aber was mich jetzt abschließend mal interessieren würde:

    Ist das ganze Programm denn so falsch? Ich denke immer, dass da doch nicht mehr so viel fehlen kann, um die Sache ans laufen zu kriegen. Oder doch?

    Dom



  • Es sind viele kleine Dinge die zeigen das du nicht weißt was du tust.

    In "Rund um die Programmierung" war ein Link (http://www.c-plusplus.net/forum/281353). Da wird versucht Fahrzeuge durch Versuch und Irrtum zu bauen.
    Die meisten Fahrzeuge fahren irgendwie.
    Du könntest mit deiner Erfahrung sofort ein Fahrzeug bauen das vernünftig fährt.

    Genauso ist dein Programm irgendwie nicht so falsch.



  • Vielleicht hilft dir das für das Verständnis, leider ohne gescheite Funktionen: http://www.gia.rwth-aachen.de/Lehre/Cpp/script/online/node80.html



  • Ich hab aus Langeweile mal eine zirkuläre, doppelt verkettete Liste für ints zusammengehackt... vielleicht kannst du dir davon was abschauen und die Konzepte besser verstehen:

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef struct list_element {
    	int data;
    	struct list_element *prev, *next;
    } list_element;
    
    typedef struct {
    	list_element *head;
    	long count;
    } list;
    
    list* create_list() {
    	list *l = malloc(sizeof(list));
    	if (l == 0)
    		return 0;
    
    	l->head = 0;
    	l->count = 0;
    	return l;
    }
    
    list_element* create_list_element(int data) {
    	list_element* le = malloc(sizeof(list_element));
    	if (le == 0)
    		return 0;
    
    	le->data = data;
    	le->prev = le->next = 0;
    	return le;
    }
    
    void destroy_list(list *l) {
    	if (l == 0)
    		return;
    
    	//Delete the elements first
    	if (l->head != 0) {
    		list_element *current = l->head;
    		list_element *next = 0;
    
    		list_element *last = l->head->prev;
    		last->next = 0;		//Disable the circular attribute of the list
    
    		do {
    			next = current->next;
    			free(current);
    			current = next;
    		} while (next != 0);
    	}
    
    	free(l);
    }
    
    void add_element(list *l, list_element *le) {
    	if (le == 0)
    		return;
    
    	if (l->head == 0) {	//First element in the list
    		l->head = le;
    		l->head->next = le;
    		l->head->prev = le;
    	}
    	else {
    		list_element *last = l->head->prev;
    		last->next = le;
    		le->prev = last;
    
    		le->next = l->head;
    		l->head->prev = le;
    	}
    
    	++l->count;
    }
    
    int main(int argc, char **argv) {
    	int i = 0;
    	list *l;
    	list_element *head;	
    
    	l = create_list();	
    	for (i = 0; i < 10; ++i) {
    		add_element(l, create_list_element(i));
    	}
    
    	printf("Element count: %d\n", l->count);
    
    	head = l->head;
    	do {
    		printf("Data: %d\n", head->data);
    		head = head->next;
    	} while (head != l->head);
    
    	destroy_list(l);
    	return 0;
    }
    

    Bei einer zirkulären Liste ist es so, dass der "next"-Zeiger des letzten Elements auf das erste Element der Liste zeigt. Dann braucht man auch keinen "last"-Zeiger 😉 Und wenn man nicht aufpasst, kann man immer schön im Kreis iterieren 🤡
    Was man noch machen müsste, wäre das Listenelement generisch gestalten, aber void* will ich dir jetzt nicht zumuten. Und mir so früh am morgen auch nicht 😃



  • Vielen Dank für die Antworten. Ich habe das Programm in der Uni laufen lassen und da geht es bis zu einem bestimmten Punkt. Das Problem ist das Abbruchkriterium der While Schleife. Da stimmt was nicht. Ich werde aber weiter versuchen, Eure Anmerkungen einzubauen.



  • In dem While Abbruchkriterium muss ein Fehler sein. Ich vrsuche schon die ganze Zeit zu Debuggen, aber dauernd stürzt der Editor ab. Vielleicht kann mir da jemand von euch weiterhelfen.

    int main(void){
    
    char c[30];
    int anzahl;
    anfang_ende_liste liste;
    char ende[]="ende";
    
    do{
    
        printf("Geben sie Land und Bevoelkerungsgroesse ein\n");
        scanf("%s %d",c,&anzahl);
    
        einfuegen(&liste,c,anzahl);
    
    }
    while(strcmp(ende,c) !=0);
    
    ausgeben(liste);
    
    return 0;
    }
    


  • Ich sehe keinen direkten Fehler (außer wenn Land > 30 Buchstaben), der zum Absturz führt. Vielleicht postest du mal den gesamten Code.

    ps: Bevor du einfügst prüf auch mal ob der Benutzer nicht ende eingegeben hat.



  • Um den Fehler zu finden habe ich mal das hier in der Uni versucht. Und die Ausgabe, die bei jeder Schleife erfolgt, ist eigentlich ok. Nur wenn man dann "ende" eingibt hängt er sich auf.

    int main(void){
    
      char c[30];
      int anzahl;
      anfang_ende_liste liste;
      char ende[]="ende";
    
      do{
    
        printf("Geben sie Land und Bevoelkerungsgroesse ein\n");
        scanf("%s %d",c,&anzahl);
    
        einfuegen(&liste,c,anzahl);
        ausgeben(liste);                    /*<----Test*/
    
      }
      while(strcmp(ende,c) !=0);
      ausgeben(liste);
    
      return 0;
    }
    

    Hier ist der gesamte (gute alte) code.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct Knot* Liste;
    
    typedef struct
        {
            char Name[30];
            int Einwohner; /*Einwohner in Millionen*/
    
        }Land;
    
    typedef struct Knot
        {
            Land eintrag;
            Liste vorgaenger;
            Liste nachfolger;
        }Knoten;
    
    typedef struct
        {
            Liste anfang;
            Liste ende;
        }anfang_ende_liste;
    
    void einfuegen(anfang_ende_liste* liste, char c, int anzahl){
    
        if(liste->anfang==NULL){
            Liste tmp=(Liste) malloc(1*sizeof(Knoten));
            liste->anfang=tmp;
            liste->ende=tmp;
            liste->anfang->nachfolger=NULL;
            liste->anfang->vorgaenger=NULL;
            strcpy(tmp->eintrag.Name, c);
            tmp->eintrag.Einwohner=anzahl;
        }
    
        if (anzahl<100){
            Liste tmp=(Liste) malloc(1*sizeof(Knoten));
            strcpy(tmp->eintrag.Name, c);
            tmp->eintrag.Einwohner=anzahl;
            tmp->vorgaenger=NULL;
            tmp->nachfolger=liste->anfang;
            liste->anfang=tmp;
            tmp->nachfolger->vorgaenger=tmp;
        }
    
        if (anzahl>=100){
            Liste tmp=(Liste) malloc(1*sizeof(Knoten));
            strcpy(tmp->eintrag.Name, c);
            tmp->eintrag.Einwohner=anzahl;
            tmp->nachfolger=NULL;
            tmp->vorgaenger=liste->ende;
            liste->ende=tmp;
            tmp->vorgaenger->nachfolger=tmp;
    
        }
    
    }
    
    void ausgeben(anfang_ende_liste liste){
        Liste tmp=liste.anfang;
        while (tmp!=0){
            printf("%s %d", tmp->eintrag.Name, tmp->eintrag.Einwohner);
            tmp=tmp->nachfolger;
    
        }
    }
    
    int main(void){
    
    char c[30];
    int anzahl;
    anfang_ende_liste liste;
    char ende[]="ende";
    
    do{
    
        printf("Geben sie Land und Bevoelkerungsgroesse ein\n");
        scanf("%s %d",c,&anzahl);
        einfuegen(&liste,c,anzahl);   
    
    }
    while(strcmp(ende,c) !=0);
    
    ausgeben(liste);
    
    return 0;
    }
    


  • Ich habe den Code jetzt auf einem anderen Rechner laufen lassen weil ich mir dachte, dass es vielleicht ein Problem ist, dass ich ein 64bit System habe (ganz naiv)

    Auf jeden Fall sehe ich jetzt mehr Fehlermeldungen.

    Bei den stringcopy aktionen läuft auch was schief.

    invalid conversion from car to const char*

    initializing argument 2 of char* strcpy(char*, const char*)

    mal sehen...



  • Den ersten Fehler findest du schon in den Übergabeparametern von einfuegen...



  • vielleicht habe ich da den stern vergessen

    einfuegen(&liste,*c,anzahl);



  • Jetzt werden auch auf meinem Laptop keine errors mehr angzeigt. und das Programm stürzt ab. Ok. Ich geb auf. Aber vielen Dank für Eure Hilfe. Ich hab trotzdem super viel dazu gelernt. Besonders durch die geposteten Beispiele.

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct Knot* Liste;
    
    typedef struct
        {
            char Name[30];
            int Einwohner; /*Einwohner in Millionen*/
    
        }Land;
    
    typedef struct Knot
        {
            Land eintrag;
            Liste vorgaenger;
            Liste nachfolger;
        }Knoten;
    
    typedef struct
        {
            Liste anfang;
            Liste ende;
        }anfang_ende_liste;
    
    void einfuegen(anfang_ende_liste* liste, char c, int anzahl){
    
        if(liste->anfang==NULL){
            Liste tmp=(Liste) malloc(1*sizeof(Knoten));
            liste->anfang=tmp;
            liste->ende=tmp;
            liste->anfang->nachfolger=NULL;
            liste->anfang->vorgaenger=NULL;
            strcpy(tmp->eintrag.Name, &c);
            tmp->eintrag.Einwohner=anzahl;
        }
    
        if (anzahl<100){
            Liste tmp=(Liste) malloc(1*sizeof(Knoten));
            strcpy(tmp->eintrag.Name, &c);
            tmp->eintrag.Einwohner=anzahl;
            tmp->vorgaenger=NULL;
            tmp->nachfolger=liste->anfang;
            liste->anfang=tmp;
            tmp->nachfolger->vorgaenger=tmp;
        }
    
        if (anzahl>=100){
            Liste tmp=(Liste) malloc(1*sizeof(Knoten));
            strcpy(tmp->eintrag.Name, &c);
            tmp->eintrag.Einwohner=anzahl;
            tmp->nachfolger=NULL;
            tmp->vorgaenger=liste->ende;
            liste->ende=tmp;
            tmp->vorgaenger->nachfolger=tmp;
    
        }
    
    }
    
    void ausgeben(anfang_ende_liste liste){
    
        Liste tmp=liste.anfang;
        while (tmp!=0){
            printf("%s %d", tmp->eintrag.Name, tmp->eintrag.Einwohner);
            tmp=tmp->nachfolger;
    
        }
    }
    
    int main(void){
    
    char c[30];
    int anzahl;
    anfang_ende_liste liste;
    char ende[]="ende";
    
    do{
    
        printf("Geben sie Land und Bevoelkerungsgroesse ein\n");
        scanf("%s %d",c,&anzahl);
    
        einfuegen(&liste,*c,anzahl);
    
    }
    while(strcmp(ende,c) !=0);
    
    ausgeben(liste);
    
    return 0;
    }
    


  • Ich habe dir einige Fehler berichtigt, es können (und sind wahrscheinlich) noch genug Fehler für dich übrig.

    #include <stdio.h> 
    #include <stdlib.h> 
    #include <string.h> 
    
    typedef struct Knot* Liste; 
    
    typedef struct 
    { 
        char Name[30]; 
        int Einwohner;
    }Land; 
    
    typedef struct Knot 
    { 
        Land eintrag; 
        Liste vorgaenger; 
        Liste nachfolger; 
    }Knoten; 
    
    typedef struct 
    { 
        Liste anfang; 
        Liste ende; 
    }anfang_ende_liste; 
    
    void einfuegen(anfang_ende_liste* liste, char *c, int anzahl){ //muss char* sein da es sonst nur ein Zeichen ist
    
        if(liste->anfang==NULL){ 
            Liste tmp=(Liste) malloc(1*sizeof(Knoten)); 
            liste->anfang=tmp; 
            liste->ende=tmp; 
            liste->anfang->nachfolger=NULL; 
            liste->anfang->vorgaenger=NULL; 
            strcpy(tmp->eintrag.Name, c); //darf nicht die Adresse übergeben Pointer ist schon die Adresse
            tmp->eintrag.Einwohner=anzahl; 
        } 
        else if (anzahl<100){ //else if sonst wird ein element mehrfach erzeugt
            Liste tmp=(Liste) malloc(1*sizeof(Knoten)); 
            strcpy(tmp->eintrag.Name, c); //darf nicht die Adresse übergeben Pointer ist schon die Adresse
            tmp->eintrag.Einwohner=anzahl; 
            tmp->vorgaenger=NULL; 
            tmp->nachfolger=liste->anfang; 
            liste->anfang=tmp; 
            tmp->nachfolger->vorgaenger=tmp; 
        } 
    	else{ //else nicht immer if
            Liste tmp=(Liste) malloc(1*sizeof(Knoten)); 
            strcpy(tmp->eintrag.Name, c); //darf nicht die Adresse übergeben Pointer ist schon die Adresse
            tmp->eintrag.Einwohner=anzahl; 
            tmp->nachfolger=NULL; 
            tmp->vorgaenger=liste->ende; 
            liste->ende=tmp; 
            tmp->vorgaenger->nachfolger=tmp; 
    
        } 
    } 
    
    void ausgeben(anfang_ende_liste *liste){ //Übergabe des pointers keine kopie
        Liste tmp=liste->anfang; //Operator ersetzt wegen pointer
        while (tmp){ 
            printf("%s %d", tmp->eintrag.Name, tmp->eintrag.Einwohner); 
            tmp=tmp->nachfolger; 
        } 
    } 
    
    int main(){ 
    	char c[30]; 
    	int anzahl; 
    	anfang_ende_liste liste = {0}; //Strukt mit 0 initialisieren, sonst scheitert die Prüfung der Zeiger auf NULL mit Programmabsturz
    	char ende[]="ende"; 
    
    	do{ 
    		printf("Geben sie Land und Bevoelkerungsgroesse ein\n"); 
    
    		if(2 == scanf("%s%d",c,&anzahl)) //Prüfe zusätzlich ob auch 2 Eingaben gemacht wurden
    			einfuegen(&liste,c,anzahl); 
    	} while(strcmp(ende,c) !=0); 
    
    	ausgeben(&liste); //adresse wegen pointer
    
    	return 0; 
    }
    


  • Vielen Dank für die Antwort. Mir ist aber einfach folgendes nicht klar:

    In der uni habe ich ja diesen Test gemacht:

    int main(void){
    
      char c[30];
      int anzahl;
      anfang_ende_liste liste;
      char ende[]="ende";
    
      do{
    
        printf("Geben sie Land und Bevoelkerungsgroesse ein\n");
        scanf("%s %d",c,&anzahl);
    
        einfuegen(&liste,c,anzahl);
        ausgeben(liste);                    /*<----Test*/
    
      }
      while(strcmp(ende,c) !=0);
      ausgeben(liste);
    
      return 0;
    }
    

    Mit meiner alten Version des Programms. Dann kam die Eingabeaufforderung "Geben sie Land.............". Wenn ich dann "Deutschland 80" eingegeben habe kamm die Ausgabe deutschland100. Dann kam wieder die Aufforderung und ich habe eigegeben "Italien 70" und es kam raus: italien70 deutschland 80. Es hat als wirklich geklappt. Das kann docj eigentlich nicht sein, oder?



  • Kann schon sein, wenn der Compiler in der Uni die Strukt automatisch auf 0 gesetzt hat und deiner daheim nicht(was korrekt ist). Da die Strukt nicht 0 ist kann dort jeder Wert stehen und somit schlug deine Prüfung fehl.



  • noch eine frage: greift das abbruchkriterium nicht doch falsch? scanf erwartet doch einen string und eine zahl. außerdem wird doch ende noch in die liste eingefügt, weil er mit do dirket in die schleife reingeht und der abbruch erst am ende erfolgt



  • Da fehlt halt noch mindestens eine Überprüfung damit ende nicht eingefügt wird. Es gibt immer etwas zu verbessern und ich habe gerade nur das Nötigste verbessert.


Anmelden zum Antworten