Verkettete dyn. Listen ohne Hilfszeiger?
-
Johann2 schrieb:
Die Funktion soll doch aber nur einmal aufgerufen werden, alle Daten einlesen und dementsprechend nur einmal pperson_t zurückgeben.
Ja, ok. Ich würde das aber nicht alles in eine Funktion reinstopfen, sondern
die Eingabeaufforderung, Eingabe, Allokation in separate Funktionen auslagern.
Ein Array vorzubelegen und deshalb die Datesätze alle umzukopieren ist IMO auch nicht gerade toll, aber was solls.
Nun gut, nehmen wir eine Funktion und packen all die schönen Sachen da rein. Ich habe deinen Code ein bisschen bearbeitet und u.a. um die gewünschte Rückgabe ergänzt, guckst du, was du davon verwerten kannst:void cb() { // clear buffer int c = 0; while ( c = getchar() != '\n' && c != EOF ) {} } void view_input ( pperson_t p ) { unsigned i = 0; if ( p == NULL ) return; puts("**************************************************"); puts("************* Anzeige der Datensaetze ************"); puts("**************************************************"); while ( p[i].fNam[0] != EMPTYTAG_C ) { printf ( "%s\n", p[i].fNam ); printf ( "%s\n", p[i].vNam ); printf ( "%u %u %u", p[i].gebDat.gdTag, p[i].gebDat.gdMonat, p[i].gebDat.gdJahr ); printf ( "%u\n", p[i].identNr ); puts("**************************************************"); i++; } } pperson_t ReadPerFromKbd( void ) { pperson_t parr = NULL; // Alle Strukturelemente auf 0, inklusive Nullterminierung der char Arrays. person_t PerArr[PERANZMAX+1] = {0}; unsigned i = 0, n = 0; // Index und Anzahl der eingegebenen Datensätze. char* p = NULL; // Hilft, das '\n' zu entfernen. char nam_fmt[256] = {0}; sprintf ( nam_fmt, "%%%us", NAMLEN ); puts ( "Eingabe von Personendaten (Ende: leerer Familienname)." ); printf ( "Maximale Anzahl der einzulesenden Datensaetze: %u\n", PERANZMAX ); while ( 1 != scanf ( "%u", &n )) { printf ("Fehler in der Eingabe! Noch einmal: " ); cb(); }cb(); if ( n > PERANZMAX ) // Personenanzahl ggf. begrenzen. printf ("Die Anzahl wurde auf %u reduziert.\n", n = PERANZMAX ); for( i = 0; i < n; i++ ) { printf ("Familienname: "); fgets ( PerArr[i].fNam, NAMLEN, stdin ); if ( NULL == ( p = strrchr ( PerArr[i].fNam, '\n' ))) // Ist der Eingabepuffer leer? cb(); // Nein, es wurden mehr als NAMLEN-2 Zeichen eingegeben, Puffer leeren. if ( PerArr[i].fNam[0] == '\n' ) { // Wurde die Eingabe beendet? PerArr[i].fNam[0] = EMPTYTAG_C; // Jepp, Ende der Eingabe kennzeichnen. break; } else { *p = 0; // Das '\n' Zeichen aus dem Nachnamen entfernen. } printf("Vorname: "); scanf ( nam_fmt, PerArr[i].vNam ); cb(); printf("Geburtstag ( TT MM JJ ): "); while ( 3 != scanf ( "%u %u %u", &PerArr[i].gebDat.gdTag, &PerArr[i].gebDat.gdMonat, &PerArr[i].gebDat.gdJahr )) { printf ("Fehler in der Eingabe! Noch einmal: " ); cb(); }cb(); printf("Personal-Nr.: "); while ( 1 != scanf ( "%u", &PerArr[i].identNr )) { printf ("Fehler in der Eingabe! Noch einmal: " ); cb(); }cb(); } PerArr[i].fNam[0] = EMPTYTAG_C; if ( NULL == ( parr = calloc ( i+1, sizeof(*parr) ))) { puts ( "Kein Arbeitsspeicher!" ); return NULL; } memcpy ( parr, PerArr, (i+1) * sizeof(*parr) ); return parr; } int main() { pperson_t parr = ReadPerFromKbd(); view_input ( parr ); free ( parr ); return 0; }
Gruß,
B.B.
-
Da ist noch ne böse Fall im else Zweig, wenn mehr als NAMELEN-2 Zeichen eingegeben werden. Es fehlt die Prüfung auf NULL, sonst gibts nen Absturz:
... else { if ( *p != NULL ) *p = 0; // Das '\n' Zeichen aus dem Nachnamen entfernen. } ...
-
Noch ein kleiner Bug
Damit das Array nicht gesprengt werden kann, sollte man den Formatstring so anlegen:sprintf ( nam_fmt, "%%%us", NAMLEN - 1 ); :warning: :p
-
Najaaa ... andererseits, nimmt man NAMELEN als Bedeutung namelength ernst, schreibt man
typedef char nam_t [NAMLEN+1];
damit der Name auch wirklich NAMELEN Zeichen haben kann.
Dann lässt man den Salat stehen wie er war:sprintf ( nam_fmt, "%%%us", NAMLEN );
Besser ist das, Bruder.
-
@Big Brother
LOLOL dickes Dankesoviel Zeitaufopferung für meine Frage habe ich nicht erwartet
if ( NULL == ( parr = calloc ( i+1, sizeof(*parr) ))) { puts ( "Kein Arbeitsspeicher!" ); return NULL; } memcpy ( parr, PerArr, (i+1) * sizeof(*parr) ); return parr;
Das löst mein Problem
die Funktion memcpy() war mir bis jetzt noch nicht bekannt.
Nur frage ich mich warum denn auch eintypedef pperson_t* ppperson_t; /* Typname: Zeiger auf Zeiger auf Struktur */
in der Headerdatei steht, die wir verwenden sollen.
Dein Code hat mich gestern noch bischen beschäftigt, da sind mir paar dinge noch unklar:
char nam_fmt[256] = {0}; sprintf ( nam_fmt, "%%%us", NAMLEN );
Was genau bewirkt "%%%us" ?
while ( c = getchar() != '\n' && c != EOF ) {}
Wozu das EOF, was macht denn das?
*p = 0; // Das '\n' Zeichen aus dem Nachnamen entfernen.
Ein Char im String bekommt den int Wert 0?
while ( 1 != scanf ( "%u", &n )) { printf ("Fehler in der Eingabe! Noch einmal: " ); cb(); }cb();
Also das find ich klasse
Frage mich nur noch wie man führende White Space Zeichen auch abfängt.. (aba das krieg ich notfalls auch wo anders raus^^)
Und:
Ein Array vorzubelegen und deshalb die Datesätze alle umzukopieren ist IMO auch nicht gerade toll, aber was solls.
Geht das denn auch eleganter?
Gruß
Johann
-
Johann2 schrieb:
char nam_fmt[256] = {0}; sprintf ( nam_fmt, "%%%us", NAMLEN );
Was genau bewirkt "%%%us" ?
%% gibt ein einzelnes % aus, %u sagt 'printf', dass ein unsigned-wert ausgegeben werden soll und das 's' hängt einfach nur ein s an. die ausgabe müsste etwa so aussehen, wenn NAMELEN 3 ist:
%3s
-
Johann2 schrieb:
Was genau bewirkt "%%%us" ?
will man das % in einem string haben, muss man es mit dem gleichen zeichen maskieren. guckst du auch frickys antwort.
Johann2 schrieb:
Nur frage ich mich warum denn auch ein
typedef pperson_t* ppperson_t; /* Typname: Zeiger auf Zeiger auf Struktur */
in der Headerdatei steht, die wir verwenden sollen.
das ist eine weitere möglichkeit, es werden die zeiger auf die strukturen in einem zeigerarray gespeichert. d.h. zu jedem datensatz wird ein weiterer zeiger benötigt. guckst du auch big brothers erste antwort.
Johann2 schrieb:
while ( c = getchar() != '\n' && c != EOF ) {}
Wozu das EOF, was macht denn das?
manche systeme haben als zeilenende angeblich ein EOF.
Johann2 schrieb:
*p = 0; // Das '\n' Zeichen aus dem Nachnamen entfernen.
Ein Char im String bekommt den int Wert 0?
Das '\n' wird durch die 0 ersetzt, dadurch wird der zeilenumbruch aus dem array entfernt. strlen liefert dann einen um 1 kleineren wert.
Johann2 schrieb:
Also das find ich klasse Frage mich nur noch wie man führende White Space Zeichen auch abfängt.. (aba das krieg ich notfalls auch wo anders raus^^)
das array einfach inplace umkopieren.
Johann2 schrieb:
Und:
Ein Array vorzubelegen und deshalb die Datesätze alle umzukopieren ist IMO auch nicht gerade toll, aber was solls.
Geht das denn auch eleganter?
vor jeder eingabe eines datensatzes speicher anfordern und die eingabe gleich abspeichern. den zeiger in eine verkettete liste oder in deinem ppperson zeigerarray speichern.
-
hier is noch ein bug, den ich nicht rauskriege:
printf("\n\nPerArr[0].fNam ist %s",PerArr[0].fNam); //wird richtig angezeigt printf("\n\nPerArr[1].fNam ist %s",PerArr[1].fNam); //wird richtig angezeigt printf("\n\nPerArr[2].fNam ist %s",PerArr[2].fNam); //wird richtig angezeigt if ( NULL == ( pPerArr = (pperson_t)calloc( iAktP+1, sizeof(person_t) ))) { puts ( "Kein Arbeitsspeicher!" ); return NULL; } memcpy( pPerArr, PerArr, (iAktP+1) * sizeof(pPerArr) ); printf("\n\npPerArr[0].fNam ist %s",pPerArr[0].fNam); //wird richtig angezeigt printf("\n\npPerArr[1].fNam ist %s",pPerArr[1].fNam); //ab hier wird printf("\n\npPerArr[2].fNam ist %s",pPerArr[2].fNam); //nichts mehr angezeigt return pPerArr; }/* ReadPerFromKbd() */
Stimmt was beim memcpy() nicht, oder is meine Ansprache auf die dyn. Strukturkomponenten falsch? Sieht so aus als ob nur das erste Feldelement pPerArr[0] kopiert wird und die anderen nicht.
Gruß
Johann
-
Es wird nichts angezeigt, weil zu wenig kopiert wird, es werden nur sizeof(pPerArr) Bytes kopiert (lass dir die Größe mit printf anzeigen), das ist zu wenig. Du musst beim Kopieren den Zeiger dereferenzieren:
memcpy( pPerArr, PerArr, (iAktP+1) * sizeof(*pPerArr) );
-
4534kj schrieb:
Johann2 schrieb:
while ( c = getchar() != '\n' && c != EOF ) {}
Wozu das EOF, was macht denn das?
manche systeme haben als zeilenende angeblich ein EOF.
dieses EOF ist ein künstliches 'nicht-zeichen', es steckt nicht in der datei oder am zeilenende, sondern wird von 'getchar' selbst erzeugt. deshalb gibt 'getchar' auch einen 'int' zurück. normale zeichen sind 0...255 und EOF liegt ausserhalb davon.