Problem mit Doppelt verketteter Liste
-
Hi, habe eine doppelt verkettete Liste programmiert, mit anfangs- und ende- adresse. syntax ist soweit in ordnung, aber die ausgabe ergibt für mich keinen Sinn
...
Hier der Quellcode:/* Dateiname: malloc1.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>typedef struct produkt {
char name [20];
double preis;
} ;typedef struct liste {
struct produkt * daten;
struct liste * previous;
struct liste * next;
} ;struct liste * neuerKnoten (void)
{
struct liste * p = (struct listemalloc (sizeof(struct liste));
if (p == NULL)
{
printf("Fehler! Es konnte kein Speicher bereitgestellt werden!");
exit(1);
}
return p;
}void printList ( struct liste * startelement )
{
while (startelement != NULL)
{
printf("Produktname: %s Preis: %6f \n", startelement->daten->name, startelement->daten->preis);
startelement = startelement ->next;
}}
int main (void)
{int i , produkte;
struct liste *anfang = NULL;
struct liste *ende = NULL;
struct liste *produktliste;
struct liste *hilfspointer;printf("Wieviele Produkte sollen eingelesen werden?\n");
scanf("%d", &produkte);for (i = 1; i <= produkte; i++)
{
if (anfang == NULL) // Liste noch leer, kein Startelement
{
produktliste = neuerKnoten();anfang = produktliste;
ende = produktliste;produktliste -> previous = NULL;
produktliste -> next = NULL;
}
else // Liste besitzt mind. 1 Element
{
hilfspointer = neuerKnoten();produktliste -> next = hilfspointer;
hilfspointer -> previous = produktliste;
produktliste = hilfspointer;
produktliste -> next = NULL;
ende = produktliste;
}
printf ("\n\nGib den Produktnamen ein: ");
scanf ( "%s" , (produktliste->daten->name) );printf ("\nGib den Preis in Euro ein: ");
scanf ("%lf", &(produktliste -> daten -> preis));}
printf ("\n\n----------------------------\n");
printf ("AUSGABE DER LISTE \n ");
printf ("----------------------------\n\n");printList(anfang);
return 0;
}Was ist daran Falsch? Bei der Ausgabe wird immer nur das zuletzt eingegebene "Produkt" ausgegeben:
Wieviele Produkte sollen eingelesen werden?
3Gib den Produktnamen ein: ich
Gib den Preis in Euro ein: 10
Gib den Produktnamen ein: du
Gib den Preis in Euro ein: 5.2
Gib den Produktnamen ein: er
Gib den Preis in Euro ein: 3.33
----------------------------
AUSGABE DER LISTE
----------------------------Produktname: er Preis: 3.330000
Produktname: er Preis: 3.330000
Produktname: er Preis: 3.330000
Drücken Sie eine beliebige Taste . . .
-
Lass dir mal die Liste nach jeder Eingabe anzeigen, dann kommst du sicher schnell dahinter. Nachdem du free() nicht verwendest, führ ich das hier lieber nicht aus.
-
okay das hab ich gemacht, es werden jedesmal die vorherigen Daten überschrieben bzw. n-mal das gleiche ausgegeben. Das versteh ich aber nicht, da ich in der main() immer ein listenelement weiterrücke, der anfangszeiger zeigt auf das startelement. bei der ausgabe übergebe ich diesen und rücke wieder immer eins weiter ( startelement = startelement -> next)..
ich komm irgendwie nicht drauf..soll ich in die ausgabe immer ein free(...) machen für jedes element??
mach ich ohne das free mein arbeisspeicher voll??
wie krieg ich die den wieder leer?
-
Na, immerhin kommt es noch zu einer Ausgabe. Muß wohl c++ - Kompilermagic sein (s. malloc).
Für "startelement->daten->name" und "startelement->daten->preis" wurde kein Speicherplatz bereitgestellt.
Ein
p->daten = malloc(sizeof(struct produkt));
könnte das Problem beheben.
-
wenn man es einfach halten will kann man auch das struct so anlegen, dann bekommst den speicher für die daten gleich beim neuerKnoten() mit. allerdings must dann den rest auch anpassen.
typedef struct produkt_s{ char name[20]; double preis; struct produkt_s *prev; struct produkt_s *next; }produkt_t;
lg lolo
-
alternativ gehts auch so;)
struct liste{ struct produkt daten; struct liste * previous; struct liste * next; };
-
spaxx999 schrieb:
mach ich ohne das free mein arbeisspeicher voll??
Ja. Jedes
p = malloc(...)
will einfree(p)
, siehe auch
http://www.c-plusplus.net/forum/viewtopic-var-t-is-206606.htmlDie Sache mit dem fehlenden Speicher für die daten-Struktur klingt verdächtig. Bring das mal in Ordnung, dann kommt man schon irgendwie dahinter.
Und bitte die Code-Hervorhebung verwenden, dann liest sich's leichter.
-
am besten packst die pointer an den anfang
struct liste{ struct liste *prev; struct liste *next; struct produkt daten; };
-
Vielen Dank für die Antworten. Klingt alles logisch. Die structures sind so vorgegeben und aus einer einfachen Liste, soll eine doppelt verkettete gemacht werden. Trotzdem Danke.
Das mit dem malloc für die produkt-Struktur leuchtet mir ein, habe dafür nun eine neue Funktion geschrieben, wobei ich das denke ich auch in die neuerKnoten_liste - Funktion dazupacken könnte... nunja, jedenfalls werden jetzt einige Fehlermeldungen ausgegeben, die alle die printList-Funktion betreffen und die ich nicht verstehe.. habe sie als Kommentare an den betreffenden Stellen hinzugefügt..
Kann mir jemand sagen, was ich da falsch gemacht habe?? Sind die free() 's okay??
Vielen Dank/* Dateiname: malloc1.c */ #include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct produkt { char name [20]; double preis; } ; typedef struct liste { struct produkt * daten; struct liste * previous; struct liste * next; } ; struct liste * neuerKnoten_liste (void) { struct liste * p = (struct liste *) malloc (sizeof(struct liste)); if (p == NULL) { printf("Fehler! Es konnte kein Speicher bereitgestellt werden!"); exit(1); } return p; } struct produkt * neuerKnoten_produkt (void) { struct produkt * p = (struct produkt *) malloc (sizeof(struct produkt)); if (p == NULL) { printf("Fehler! Es konnte kein Speicher bereitgestellt werden!"); exit(1); } return p; void printList (struct liste * startelement) /*Fehler 1 error C2143: Syntaxfehler: Es fehlt ';' vor 'Typ'*/ { struct liste * p = startelement; while (startelement != NULL) /*Fehler 2 error C2065: 'startelement': nichtdeklarierter Bezeichner*/ { printf("Produktname: %s Preis: %6f \n", startelement->daten->name, startelement->daten->preis); /*Fehler 5 error C2223: Der linke Teil von '->daten' muss auf eine Struktur/Union zeigen */ /*Fehler 7 error C2223: Der linke Teil von '->daten' muss auf eine Struktur/Union zeigen*/ /*Fehler 4 error C2065: 'startelement': nichtdeklarierter Bezeichner */ free(startelement -> daten); /*Fehler 9 error C2223: Der linke Teil von '->daten' muss auf eine Struktur/Union zeigen*/ /*Fehler 10 error C2198: "free": Nicht genügend Argumente für Aufruf. */ /*Fehler 8 error C2065: 'startelement': nichtdeklarierter Bezeichner */ free(startelement); startelement = p->next ; /*Fehler 14 error C2039: 'next': Ist kein Element von 'produkt' */ } } int main (void) { int i , produkte; struct liste *anfang = NULL; struct liste *ende = NULL; struct liste *produktliste; struct liste *hilfspointer; printf("Wieviele Produkte sollen eingelesen werden?\n"); scanf("%d", &produkte); for (i = 1; i <= produkte; i++) { if (anfang == NULL) // Liste noch leer, kein Startelement { produktliste = neuerKnoten_liste(); anfang = produktliste; ende = produktliste; produktliste -> previous = NULL; produktliste -> next = NULL; } else // Liste besitzt mind. 1 Element { hilfspointer = neuerKnoten_liste(); produktliste -> next = hilfspointer; hilfspointer -> previous = produktliste; produktliste = hilfspointer; produktliste -> next = NULL; ende = produktliste; } produktliste -> daten = neuerKnoten_produkt(); printf ("\n\nGib den Produktnamen ein: "); scanf ( "%s" , (produktliste->daten->name) ); printf ("\nGib den Preis in Euro ein: "); scanf ("%lf", &(produktliste -> daten -> preis)); } printf ("\n\n----------------------------\n"); printf ("AUSGABE DER LISTE \n "); printf ("----------------------------\n\n"); printList(anfang); return 0; }
-
Du gibst mit free Speicher frei und danach greifst du wieder darauf zu, das geht nicht.
-
Das Löschen und Anzeigen der Liste würde ich in zwei separate Funktionen aufteilen.
Vielleicht findest du hier noch die eine oder andere Anregung:#include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #define NAME_BUFSIZE 64 #define ANZAHL_ELEMENTE 2 // Anzahl der Elemente in Produkt. typedef struct produkt Produkt; typedef struct liste Liste; struct produkt { char name[NAME_BUFSIZE]; double preis; }; struct liste { Produkt* daten; Liste* prev, *next; }; int neues_element ( Liste** l ) { if ( NULL == ( *l = calloc ( 1, sizeof ( Liste )))) return 1; return 0; } int neues_produkt ( Produkt** p ) { if ( NULL == ( *p = calloc ( 1, sizeof ( Produkt )))) return 1; return 0; } void loeschi ( Liste* l ) { Liste* p; while ( l ) p = l->next, free ( l->daten ), free ( l ), l = p; } void gucki ( Liste* l ) { if ( !l ) puts ( "Liste leer, gibt nix zu sehen, Bruder!" ); while ( l ) printf ( "%s %.2lf\n", l->daten->name, l->daten->preis ), l=l->next; } int tschuessi ( Liste* l ) { loeschi ( l ); fprintf ( stderr, "%s\n", strerror( errno ) ); return 1; } int cb() { int c = 0; unsigned n = 0; while ( ( c = fgetc(stdin) ) != '\n' && c != EOF ) n++; return n; } int main() { int n = 0; char* fmt = "%63s %lf"; // Angepasst an NAME_BUFSIZE Liste* anfang = NULL, *aktuell = NULL, *neu = NULL; Produkt* produkt = NULL; printf ( "Wieviel? " ); scanf ( "%d", &n ); if ( n > 0 ) puts ( "Gib Zeugs ein." ); while ( n > 0 ) { if ( neu == NULL ) { if ( neues_element ( &neu )) return tschuessi ( anfang ); } if ( neu != NULL ) { if ( anfang == NULL ) anfang = aktuell = neu; else aktuell->next = neu, neu->prev = aktuell, aktuell = neu; if ( neues_produkt ( &produkt )) return tschuessi ( anfang ); } if ( ANZAHL_ELEMENTE != scanf ( fmt, produkt->name, &produkt->preis )) puts ( "Eingabe kagge! Nochmal! :)" ); else aktuell->daten = produkt, neu = NULL, n--; if ( cb() ) puts ( "Ein Teil der Eingabe wurde ignoriert!" ); } gucki ( anfang ); loeschi ( anfang ); return 0; }
-
Vielen Dank für den ausführlichen Beitrag und die Mühe dafür!!
Das Trennen von Ausgabe und Löschen der Liste macht Sinn.Jetzt klappts endlich.
Mich würde noch interessieren ob es notwendig ist, den Funktionen neues_element / neues_produkt als Parameter einen Pointer auf Pointer zu übergeben (z.B. Liste **l), oder ob es auch möglich ist ohne Parameterübergabe, und den freien Speicher über ein return zurückzugeben. (call by reference <-> call by value)...
gibt es einen bedeutenden Unterschied??
Wieso hast du das bei loeschi nicht so gemacht?
-
Den Zeiger per return zurückzugeben ist auch ohne weiteres Möglich. Das ist eher eine Geschmackfrage, so gefällt es mir besser. Die Abfrage auf NULL in main entfällt, der Code ist kürzer und übersichtlicher.
Bei der Anzeigefunktion habe ich es nicht gemacht, weil dem übergebenen Parameter kein Wert zugewiesen wird.
Magst Recht haben, das es konsequent wäre, es zu tun und den Zeiger nach der Freigabe des Speicherplatzes für die Liste mit der Zuweisung von NULL zu entschärfen.