Kritik. bitte.
-
Aufgabenstellung:
Jeder Kunde wird in unserem System mit folgenden Angaben geführt: - Kunden-Nummer - Vorname - Nachname Jedes Buch wird in unserem System mit folgenden Angaben geführt: - Buch-Nummer - Titel - Autor - Ausleihvermerk (welcher Kunde hat das Buch zurzeit ausgeliehen?) Das Programm soll eine einfache Menüsteuerung erhalten, die es dem Benutzer erlaubt, neue Kunden und Bücher einzugeben. Außerdem soll er eine Liste der Kunden oder Bücher anfordern können. Dabei soll das Programm erst dann beendet werden, wenn der Benutzer dies wünscht. Das könnte etwa so aussehen: Gehen Sie bei der Programmentwicklung folgendermaßen vor: - deklarieren Sie einen struct-Datentyp, der z. B. kunde heißt, mit den 3 Komponenten: Kundennummer als Ganzzahl, Vorname als String, Nachname als String. Jeder Datensatz eines Kunden, der später angelegt wird, soll von diesem Datentyp sein. - deklarieren Sie als Vorlage für einen Buch-Datensatz einen weiteren struct-Datentyp, der z. B. buch heißt, mit den 4 Komponenten: Inventarnummer als Ganzzahl, Titel und Autor jeweils als String und den Ausleihvermerk als Pointer vom Typ kunde. - zur Verwaltung der Kunden-Daten dient ein Array, das Pointer auf die Datensätze enthält. Legen Sie ein Array von Pointern an, die auf den Datentyp kunde zeigen können. Dieses Array wird unsere Kundenkartei. Mit dessen Hilfe haben wir Zugriff auf alle Daten unserer Kunden. Initialisieren Sie das Array mit NULLPointern, da die Kundenkartei ja noch keine Einträge enthält. Die Größe des Arrays bestimmt die maximale Anzahl von Kunden. - legen Sie analog dazu ein Array von Pointern an, die auf den Datentyp buch zeigen können. Initialisieren Sie auch dieses Array mit NULL-Pointern. Die Größe des Arrays bestimmt die maximale Anzahl von Büchern. Dieses Array ist der "Katalog". - schreiben Sie eine Funktion für die Kundeneingabe. Diese Funktion soll eine Datenstruktur vom Typ kunde dynamisch erzeugen (mit malloc), die Daten vom Benutzer anfordern und als Rückgabewert den Pointer auf die neue Datenstruktur liefern. Der Rückgabewert wird im Array der Kundenkartei gespeichert. - mit jedem Aufruf der Funktion wird ein neuer Kunde angelegt und eine neue Position des Kundenkartei- Array beschrieben, so dass sich die Kartei sukzessive aufbaut. - schreiben Sie eine ähnliche Funktion zum Erfassen eines neuen Buches. Buch-Nummer, Autor und Titel werden vom Benutzer eingegeben. Der Ausleihvermerk soll zu NULL initialisiert werden, da das neue Buch ja noch nicht verliehen ist. - mit jedem Aufruf dieser Funktion wird ein neues Buch in die Bibliothek aufgenommen und eine neue Position des Katalog-Array beschrieben, so dass sich der Katalog sukzessive aufbaut. - schreiben Sie eine Funktion, die die Kundenkartei ausgibt. Diese Funktion soll per "call by reference" das Kundenkartei-Array als Parameter bekommen. - schreiben Sie eine ähnliche Funktion, die den Bücherkatalog ausgibt. Diese Funktion soll per "call by reference" das Katalog-Array als Parameter bekommen. - erzeugen Sie eine einfache Menüsteuerung, die es erlaubt die Eingabe und Ausgabe beliebig oft auszuführen. Aufgabe 2.2 Das zweite Modul erweitert das System unserer Bibliothek um die Verleihabwicklung: - Eingabe: welches Buch, welcher Kunde? - Auflistung aller verliehenen Bücher Dazu erweitern wir das Menü um 2 Optionen. Beim Verleihvorgang wird der Kunde mit Hilfe der Kunden- Nummer angegeben und das Buch mit der Buch-Nummer. Dabei soll das Programm erkennen, wenn der Benutzer eine nicht existierende Nummer eingibt. Der Quellcode der ersten Aufgabe kann vollständig wiederverwendet werden. Gehen Sie bei der Programmentwicklung folgendermaßen vor: - erweitern Sie das Menü für die Eingabe eines Verleihvorgangs und die Auflistung der verliehenen Bücher - schreiben Sie eine Funktion zum Suchen in der Kundenkartei. Diese Funktion bekommt die Kunden- Nummer und das Kundenkartei-Array als Parameter übergeben. Damit sucht sie in dem Array nach dem passenden Eintrag, indem sie mit Hilfe der Pointer indirekt auf die Datensätze der Kunden zugreift und die Kunden-Nummer vergleicht. Wenn der Datensatz mit der richtigen Nummer gefunden wurde, gibt die Funktion als Rückgabewert den Pointer dieses Datensatzes zurück. War die Suche erfolglos, soll ein NULLPointer zurückgegeben werden. - schreiben Sie analog dazu eine Funktion zum Suchen in dem Bücherkatalog. Diese Funktion bekommt die Buch-Nummer und das Buchkatalog-Array als Parameter übergeben. Ansonsten arbeitet diese Funktion wie die oben. - bauen sie nun die Anweisungen für den Menüpunkt zum Ausleihen eines Buches ein: - dazu fordern Sie den Benutzer zur Eingabe der Kundennummer auf. Mit dieser Eingabe rufen Sie die Suchfunktion auf. Wird der Kunde gefunden, so bestätigt das Programm dies durch die Ausgabe des Namens. War die Suche erfolglos, wird der Fehler gemeldet und der Vorgang abgebrochen. - verfahren Sie genauso für die Eingabe des gewünschten Buches. - waren beide Eingaben erfolgreich, so kommt die bisher noch nicht benutzte Struktur-Komponente "Ausleihvermerk" des Buches ins Spiel. Sie soll anzeigen, wer (=Pointer auf den Kunden) das Buch ausgeliehen hat. Ist das Buch noch nicht ausgeliehen, so ist der Ausleihvermerk ein NULL-Pointer und Sie ersetzen diesen durch den Pointer auf den Kunden. Damit ist markiert, dass das Buch jetzt verliehen ist (Pointer ungleich NULL) und an wen (Pointer zeigt auf den Kunden). War der Vermerk jedoch schon gesetzt, so geben Sie eine Meldung aus, dass das Buch schon vergeben ist und sie brechen den Vorgang ab. - bauen Sie schließlich die Anweisungen für den Menüpunkt zum Auflisten der verliehenen Bücher ein. Dazu durchsuchen Sie den Katalog und schauen nach den Ausleihvermerken. Ist ein Kunde als Ausleiher vermerkt, so geben Sie das Buch und den Kunden aus. Außerdem zählen Sie die Anzahl der gefundenen Einträge und geben das Ergebnis zum Schluss aus.
Lösungsvorschlag:
#include <memory.h> #include <stdlib.h> #include <stdio.h> #include <string.h> #define MAX_CUSTOMERS 1024 #define MAX_BOOKS 512 #define MAX_BUFFER 128 void clear_stdin() { int ch = 0; while( ( ch = getchar() ) != '\n' && ch != EOF ); } char* my_getline( char const* request ) { char buffer[ MAX_BUFFER ]; char ch = 0; size_t input_length = 0; char *result = NULL; while( request && printf( "%s", request ), !fgets( buffer, sizeof( buffer ), stdin ) ) { fputs( "Eingabefehler, bitte wiederholen!\n", stderr ); clear_stdin(); } input_length = strlen( buffer ); if( input_length != 0 && buffer[ input_length - 1 ] == '\n' ) { buffer[ input_length - 1 ] = '\0'; --input_length; } result = malloc( input_length + 1 ); if( !result ) return NULL; return strcpy( result, buffer ); } unsigned my_get_uint( char const* request ) { unsigned result = 0; while( printf( "%s", request ), scanf( "%u", &result ) != 1 ) { fputs( "Eingabefehler, bitte wiederholen!\n", stderr ); clear_stdin(); } return result; } typedef struct customer_tag { unsigned id; char* name; char* surname; } customer_t; customer_t* create_customer( unsigned id ) { char buffer[ MAX_BUFFER ]; size_t input_length = 0; customer_t *result = NULL; if( ++id > MAX_CUSTOMERS ) return NULL; if( !( ( result = malloc( sizeof( *result ) ) ) && ( result->name = my_getline( "Name: " ) ) && ( result->surname = my_getline( "Nachname: " ) ) ) ) { fputs( "Nicht genuegend freier Arbeitsspeicher verfuegbar!", stderr ); if( result ) free( result->name ); free( result ); return NULL; } else result->id = id; return result; } void free_customers( customer_t* customers[] ) { customer_t const** current_customer = customers; for( ; *current_customer; ++current_customer ) { free( (*current_customer)->name ); free( (*current_customer)->surname ); free( *current_customer ); } } void print_customers( customer_t const* customers[] ) { customer_t const** current_customer = customers; puts( "Kundenkartei:\n" ); for( ; *current_customer; ++current_customer ) printf( "Kd-Nr.: %u\nName: %s\nNachname: %s\n\n", (*current_customer)->id, (*current_customer)-> name, (*current_customer)->surname ); } customer_t const* find_customer( unsigned id, customer_t const* customers[] ) { customer_t const** current_customer = customers; for( ; *current_customer; ++current_customer ) if( (*current_customer)->id == id ) return *current_customer; return NULL; } typedef struct book_tag { unsigned id; char* title; char* author; customer_t const* borrower; } book_t; book_t* create_book( unsigned id ) { char buffer[ MAX_BUFFER ]; book_t* result = NULL; size_t input_length = 0; if( ++id > MAX_BOOKS ) return NULL; if( !( ( result = malloc( sizeof( *result ) ) ) && ( result->title = my_getline( "Titel: " ) ) && ( result->author = my_getline( "Autor: " ) ) ) ) { fputs( "Nicht genuegend freier Arbeitsspeicher verfuegbar!", stderr ); if( result ) free( result->title ); free( result ); return NULL; } else { result->id = id; result->borrower = NULL; } return result; } void free_books( book_t* books[] ) { book_t const** current_book = books; for( ; *current_book; ++current_book ) { free( (*current_book)->title ); free( (*current_book)->author ); free( *current_book ); } } void print_books( book_t* books[], unsigned only_borrowed ) { book_t **current_book = books; unsigned borrowers = 0; puts( "Buecherkatalog:\n" ); for( ; *current_book; ++current_book ) { if( !only_borrowed || ( only_borrowed && (*current_book)->borrower ) ) { printf( "Buch Nr.: %u", (*current_book)->id ); if( (*current_book)->borrower != NULL ) { printf( " ist zur Zeit ausgeliehen von \"%s %s\" (Kundennummer: %u)", (*current_book)->borrower->name, (*current_book)->borrower->surname, (*current_book)->borrower->id ); ++borrowers; } printf( "\nTitel: %s\nAutor: %s\n\n", (*current_book)->title, (*current_book)->author ); } } printf( "Insgesamt sind %u Buecher verliehen.\n", borrowers ); } book_t* find_book( unsigned id, book_t const* books[] ) { book_t const** current_book = books; for( ; *current_book; ++current_book ) if( (*current_book)->id == id ) return (book_t*) *current_book; return NULL; } int main( void ) { customer_t* customers[ MAX_CUSTOMERS + 1 ] = { NULL }; size_t num_customers = 0; book_t* books[ MAX_BOOKS + 1 ] = { NULL }; size_t num_books = 0; int choice = 0; unsigned id = 0; customer_t const* found_customer = NULL; book_t* found_book = NULL; do { puts( " [1]\tNeuer Kunde\n [2]\tNeues Buch\n [3]\tKundenkartei auflisten\n [4]\tBuecherkatalog auflisten\n [5]\tBuch verleihen\n [6]\tVerliehene Buecher auflisten\n [0]\tProgramm beenden\n" ); choice = scanf( " %u", &choice ) == 1 ? choice : UINT_MAX; clear_stdin(); puts( "\n\n" ); switch( choice ) { case 1: /* Neuer Kunde */ if( num_customers < MAX_CUSTOMERS && ( customers[ num_customers ] = create_customer( num_customers ) ) ) ++num_customers; else fprintf( stderr, "\nDas Anlegen des Kunden ist fehlgeschlagen, da die Maximalanzahl verwaltbarer Kunden (%u) bereits erreicht ist!\n\n", MAX_CUSTOMERS ); break; case 2: /* Neues Buch */ if( num_books < MAX_BOOKS && ( books[ num_books ] = create_book( num_books ) ) ) ++num_books; else fprintf( stderr, "\nDas Anlegen des Buches ist fehlgeschlagen, da die Maximalanzahl verwaltbarer Buecher (%u) bereits erreicht ist!\n\n", MAX_BOOKS ); break; case 3: /* Kundenkartei auflisten */ print_customers( customers ); break; case 4: /* Buecherkatalog auflisten */ print_books( books, 0 ); break; case 5: /* Buch verleihen */ if( !( found_customer = find_customer( id = my_get_uint( "Bitte geben Sie die Kundennummer ein:" ), customers ) ) ) { fprintf( stderr, "Es existiert kein Kunde mit Kundennummer %u!\n", id ); break; } printf( "\nDer Kunde \"%s %s\" wurde unter der Kundennummer %u gefunden.\n\n", found_customer->name, found_customer->surname, found_customer-> id ); if( !( found_book = find_book( id = my_get_uint( "Bitte geben Sie die Buchnummer ein:" ), books ) ) ) { fprintf( stderr, "Es existiert kein Buch mit Buchnummer %u!\n", id ); break; } printf( "\nDas Buch \"%s\" wurde unter der Buchnummer %u gefunden.\n\n", found_book->title, found_book-> id ); if( found_book->borrower ) { printf( "Das Buch \"%s\" (Nummer: %u) wurde bereits vom Kunden \"%s %s\" (Kundennummer: %u) ausgeliehen.\n\n", found_book->title, found_book->id, found_book->borrower->name, found_book->borrower->surname, found_book->borrower->id ); } else { found_book->borrower = found_customer; printf( "Das Buch \"%s\" (Nummer: %u) ist nun als vom Kunden \"%s %s\" (Kundennummer: %u) ausgeliehen markiert.\n\n", found_book->title, found_book->id, found_book->borrower->name, found_book->borrower->surname, found_book->borrower->id ); } break; case 6: /* Verliehene Buecher auflisten */ print_books( books, 1 ); break; case 0: /* Programm beenden */ puts( "\nDas Programm wird beendet.\n" ); break; default: fputs( "Eingabefehler, bitte wiederholen!\n", stderr ); } puts( "\n\n" ); } while( choice ); free_books( books ); free_customers( customers ); }
Wutz schrieb:
Viel zu viel Code für die kleine Aufgabe und fehlerhaft und standardinkonform obendrein.
Möchte das bitte jemand etwas detaillierter ausführen?
-
Viel zu viel Code zum kommentieren.
Ein #include <limits.h> fehlt.
Einige unsichere Konvertierungen der Form T** -> const T**
das ist in C allerdings erlaubt.
-
- memory.h ist kein Standard
- my_get_uint scheitert bei simplen Nutzerfehleingaben (für z.B. Kunden/Buchnummer 123a schlägt das 'a' durch auf den folgenden Programmablauf)
-
Viel weniger Code, dafür strikt konform (bis auf eine Stelle, wobei ich bezweifle, dass du sie findest).
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct { struct kunde {int nr;char vname[100],nname[100];} *kartei[100]; struct buch {int nr;char titel[100],autor[100];struct kunde *ausleih;} *katalog[100]; int kz,bz; } Datenbank; int eingabe(int *z,int *nr,char *s1,char *s2,const char *text) { char s[300]; printf("\n%s: ",text); if( fgets(s,300,stdin) && sscanf(s,"%99s%99s",s1,s2)==2 ) *nr=++*z; if(!strchr(s,'\n')){ int c; while( (c=getchar())!='\n' && c!=EOF ); } return *nr; } int eingabeNr(char *s) { int r; printf(s); if( 1!=scanf("%d",&r) || r<1 ) r=0; { int c; while( (c=getchar())!='\n' && c!=EOF ); } return r; } void ausgabe(void *liste,int z) { struct {int i;char str1[100],str2[100];} **l=liste; while( z-- ) printf("%3d%15s%15s\n",l[z]->i,l[z]->str1,l[z]->str2); } void dbfree(Datenbank *db) { while( db->kz-- ) free(db->kartei[db->kz]); while( db->bz-- ) free(db->katalog[db->bz]); } int main() { Datenbank db={0}; char str[BUFSIZ]; while(1) switch( puts( "\n [1]\tNeuer Kunde\n [2]\tNeues Buch\n [3]\tKundenkartei auflisten\n [4]\tBuecherkatalog auflisten\n [5]\tBuch verleihen\n [6]\tVerliehene Buecher auflisten\n sonst\tProgramm beenden\n" ), fgets(str,BUFSIZ,stdin), *str ) { case '1': if( db.kz<100 ) if( db.kartei[db.kz]=calloc(1,sizeof(**db.kartei)), !eingabe(&db.kz,&db.kartei[db.kz]->nr,db.kartei[db.kz]->vname,db.kartei[db.kz]->nname,"Eingabe-Kunde: Vorname Nachname") ) puts("Eingabe fehlerhaft"),free(db.kartei[db.kz]); break; case '2': if( db.bz<100 ) if( db.katalog[db.bz]=calloc(1,sizeof(**db.katalog)), !eingabe(&db.bz,&db.katalog[db.bz]->nr,db.katalog[db.bz]->titel,db.katalog[db.bz]->autor,"Eingabe-Buch: Titel Autor") ) puts("Eingabe fehlerhaft"),free(db.katalog[db.bz]); break; case '3': ausgabe(db.kartei,db.kz);break; case '4': ausgabe(db.katalog,db.bz);break; case '5': { int kundenr=eingabeNr("KundeNr: "); if( kundenr && kundenr<=db.kz ) { int buchnr=eingabeNr("BuchNr: "); if( buchnr && buchnr<=db.bz ) { db.katalog[buchnr-1]->ausleih=db.kartei[kundenr-1]; break; } } puts("KundeNr oder BuchNr fehlerhaft"); break; } case '6': { int i; for(i=0;i<db.bz;++i) if( db.katalog[i]->ausleih ) printf("%3d%15s%15s: %3d%15s%15s\n",db.katalog[i]->nr,db.katalog[i]->titel,db.katalog[i]->autor, db.katalog[i]->ausleih->nr,db.katalog[i]->ausleih->vname,db.katalog[i]->ausleih->nname); break; } default: dbfree(&db);exit(0); } return 0; }
-
Wutz schrieb:
Viel weniger Code, dafür strikt konform (bis auf eine Stelle, wobei ich bezweifle, dass du sie findest).
Mag' ich garnicht finden. Dein Code verschluckt sich schon am Enter nach der eingabe von 1 zur Auswahl einen neuen Kunden anzulegen.
@camper: guck' ich mir an, wie auch memory.h vs. stdlib.h.
-
[cpp
fputs( "Nicht genuegend freier Arbeitsspeicher verfuegbar!", stderr );if( result )
free( result->title );free( result );
return NULL;
[/cpp]fputs( "Nicht genuegend freier Arbeitsspeicher verfuegbar!", stderr ); if( result ) { if (result->title) free( result->title ); free( result ); } return NULL;
@Wutz: So einen unübersichtlichen Code möchte auch keiner lesen.
-
@Swordfish: Benutze mal indent, um den Code schöner einzurücken, falls du Unix hast.
EDIT: Hab den Code von Swordfish mit dem vom Wutz verwechselt.^^
-
Was ist an meinen Einrückungen kaputt?
-
Och, so schlimm ist Wutz's code gar nich
Was ich schon gesehen hab, da is Wutz's seiner harmlos. ^^
-
Hab das gerade mal getestet - theoretisch sah das gut aus.
Das der Kommentar des Compilers zum 1.Quelltext:
Zu den Zeileangaben 3 hinzuaddieren.>gcc -pedantic -Os -o dbbuc002 dbbuc002.c -std=c11 dbbuc002.c: In function 'free_customers': dbbuc002.c:100:43: warning: initialization from incompatible pointer type [enabled by default] dbbuc002.c:106:9: warning: passing argument 1 of 'free' discards 'const' qualifier from pointer target type [enabled by default] In file included from dbbuc002.c:5:0: c:\mingw470\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/stdlib.h:358:38: note: expected 'void *' but argument is of type 'const struct customer_t *' dbbuc002.c: In function 'free_books': dbbuc002.c:175:35: warning: initialization from incompatible pointer type [enabled by default] dbbuc002.c:181:9: warning: passing argument 1 of 'free' discards 'const' qualifier from pointer target type [enabled by default] In file included from dbbuc002.c:5:0: c:\mingw470\bin\../lib/gcc/i686-pc-mingw32/4.7.0/../../../../include/stdlib.h:358:38: note: expected 'void *' but argument is of type 'const struct book_t *' dbbuc002.c: In function 'main': dbbuc002.c:241:58: error: 'UINT_MAX' undeclared (first use in this function) dbbuc002.c:241:58: note: each undeclared identifier is reported only once for each function it appears in dbbuc002.c:263:17: warning: passing argument 1 of 'print_customers' from incompatible pointer type [enabled by default] dbbuc002.c:111:6: note: expected 'const struct customer_t **' but argument is of type 'struct customer_t **' dbbuc002.c:275:17: warning: passing argument 2 of 'find_customer' from incompatible pointer type [enabled by default] dbbuc002.c:122:19: note: expected 'const struct customer_t **' but argument is of type 'struct customer_t **' dbbuc002.c:283:17: warning: passing argument 2 of 'find_book' from incompatible pointer type [enabled by default] dbbuc002.c:214:9: note: expected 'const struct book_t **' but argument is of type 'struct book_t **'
Das zum 2.Quelltext: Compiler wie oben, war zufrieden.
Dann hab ich versucht Johann Sebastian Bach als Kunde anzulegen - Ein bekannter Komponist - ist wohl schon zu lange her das der in Leipzig und Umgebung tätig war.Und so ein schlechter Roman mit dem Titel: "Das Grauen des Waldes" geschrieben von der "Schreiber BGB Müller, Maier und Companion" lies sich auch nicht speichern, obwohl für die Anzahl der Zeichen genug Platz vorgehalten wurde.
Ist vielleicht auch ganz gut so, das der nicht ausgeliehen werden kann.
-
Swordfish schrieb:
Was ist an meinen Einrückungen kaputt?
Dein Code war nicht gemeint.
itedvo schrieb:
Och, so schlimm ist Wutz's code gar nich
Was ich schon gesehen hab, da is Wutz's seiner harmlos. ^^Ich auch, aber:
Wutz schrieb:
Viel weniger Code, dafür strikt konform (bis auf eine Stelle, wobei ich bezweifle, dass du sie findest).
Klingt für mich irgendwie etwas arrogant. Oder täusche ich mich da?
Zumal er gar keine Funktionsargumente auf Gültigkeit überprüft (z. B. Zeiger).
-
Kritik:
- bitte keine so langen Aufgabenstellungen posten
- bitte keinen so langen sourcecode posten
- bitte die Aufgabe erst selbst angehen
- bitte nur dann die kritischen Fragen gezielt mit wenig sourcecode stellen