C-Programmierung -- Ein- und Ausgabefunktionen -- Verständnisproblem ???



  • Hallo zusammen,

    Ich lerne seit etwa einem Jahr C-Programmierung anhand des Buches C von A bis Z von Jürgen Wolf.

    Zur Zeit bin ich im 16. Kapitel - Ein-/Ausgabefunktion, welches ich schon fast durchgearbeitet habe.
    Doch nun habe ich da ein Verständnisproblem mit einem Listing im Buch.

    Hier mal das gesamte Listing zur Orientierung:

    /* dyn_text.c */
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    /* symbolische Konstanten */
    #define LINE 255
    #define ALLOC_LINE 10
    
    enum { SUCCESS, ERROR };
    
    /* Funktionsprototypen */
    int read_file(char ***, char *, int *);
    char **alloc_string_array(int, int);
    int zeilen_hinzu_fuegen(char ***, int, int, int);
    int string_anpassen(char **, int);
    int string_groesse_veraendern(char **, int, int);
    void free_string_array(char **, int);
    
    /* ***array == Ein Zeiger auf einen Zeiger einer Zeile mit
     * einem Zeiger auf die Länge der Zeile (*array[zeile][länge])
     * *fname == Name der zu öffnenden Datei
     * *zeile_n == Zeiger auf Anzahl Zeilen
     * Rückgabewert: SUCCESS wenn kein Fehler, ansonsten ERROR
     */
    
    int read_file(char ***array, char *fname, int *zeile_n)
    {
        char puffer[LINE] = { 0 };  /* Puffer zum zeilenweisen Einlesen */
        char *newline = NULL;       /* Zeiger für neue Zeile            */
        FILE *f;                    /* Datei, die geöffnet werden soll  */
        int error = SUCCESS;        /* Fehlerstatus                     */
        int zeile = 0;              /* aktuelle Zeile                   */
        int absatz_n;
    
        *zeile_n = 0;               /* erste Zeile mit 0 initialisieren */
    
        /* Speicher anfordern für ALLOC_LINE Zeilen a LINE Zeichen      */
        *array = alloc_string_array(ALLOC_LINE, LINE); // (10, 255)
    
        if(NULL != *array)
        {
            f = fopen(fname, "r");  /* Datei fname zum Lesen öffnen     */
    
            if(NULL != f)
            {
                *zeile_n = ALLOC_LINE;  /* *zeile_n = 10; */
                absatz_n = 0;
    
                /* solange kein Fehler auftritt, zeilenweise einlesen   */
                while(0 == error && NULL != fgets(puffer, LINE, f))
                {
                    newline = strchr(puffer, '\n');
    
                    if(NULL != newline)
                        /* Newline-Zeichen gegen
                         * Terminierungszeichen austauschen */
                        *newline = '\0';
    
                    strcat((*array)[zeile], puffer);
    
                    if(NULL != newline)
                    {
                        absatz_n = 1;
                        zeile++;
    
                        /* Haben wir noch Platz im Speicher
                         * für weitere Zeilen? */
                        if(zeile >= *zeile_n)
                        {
                            /* Nein, dann anfügen */
                            if(0 == zeilen_hinzu_fuegen(array, *zeile_n, ALLOC_LINE, LINE))
                                error = ERROR;
    
                            else
                                /* Anzahl der Zeilen + 10 */
                                *zeile_n += ALLOC_LINE;
                        }
                    }
    
                    /* kein Newline-Zeichen, dann Zeile länger als LINE
                     * Zeichen, String der Länge anpassen -> Speicher
                     * anfordern */
                    else
                    {
                        absatz_n++;
                        printf("%d\n", absatz_n);
    
                        if(0 == string_groesse_veraendern(*array, zeile, absatz_n * LINE))
                            error = ERROR;
                    }
                } /* while - ENDE */
    
                fclose(f);
    
                /* Wir sind am Ende vom Einlesen, oder ein Fehler trat auf.
                 * Es muss/müssen allerdings noch die übrige(n) Zeile(n) in
                 * den Speicher eingelesen werden. */
                if(0 == error && *zeile_n > zeile)
                {
                    if(0 == zeilen_hinzu_fuegen(array, *zeile_n, zeile-*zeile_n, 0))
                        error = ERROR;
    
                    *zeile_n = zeile;
                }
            }
    
            else    /* Datei fname konnte nicht geöffnet werden */
                error = ERROR;
        }
        else    /* Es konnte kein Speicher alloziert werden */
            error = ERROR;
    
        if(error != 0)
        {
            /* im Fall eines Fehlers Speicher wieder freigeben
             * und Anzahl Zeilen auf 0 setzen */
            free_string_array(*array, *zeile_n);
            *zeile_n = 0;
        }
    
        else
            string_anpassen(*array, *zeile_n);
    
        return error;
    }
    
    /*  zeilen_n == Anzahl Zeilen, wie viele reserviert werden sollen
     *  laenge  == Speicherplatz für die Länge jeder Zeile, die angefordert wird
     *  Rückgabewert: Anfangsadresse des reservierten Speichers vom String-Array (array[zeile][laenge])
     */
    
    char **alloc_string_array(int zeilen_n, int laenge)
    {
        char **array = NULL;
        int zeile;
        int ret = SUCCESS;
    
        if(zeilen_n > 0 && laenge > 0)
        {
            /* Speicher für zeilen_n Zeilen reservieren */
            array = malloc(zeilen_n * sizeof(*array));
    
            if(NULL != array)
            {
                for(zeile = 0; zeile < zeilen_n; zeile++)
                {
                    /* Für jede zeile_n Zeile laenge Bytes
                     * Speicher reservieren */
                    array[zeile] = malloc(laenge * sizeof(*array[zeile]));
    
                    if(NULL == array[zeile])
                        ret = ERROR;
    
                    else
                        /* in jeder Zeile erstes Zeichen mit \0 initialisieren */
                        array[zeile][0] = '\0';
                }
    
                if(ERROR == ret)    /* Bei Fehler Speicher freigeben */
                {
                    free_string_array(array, zeilen_n);
                    array = NULL;
                }
            }
        }
    
        return array;
    }
    
    /* ***array         == Ein Zeiger auf einen Zeiger einer Zeile mit
     * einem Zeiger auf die Länge der Zeile (*array[zeile][länge])
     * alt_n_zeichen    == Anzahl akt. Zeilen im Speicher
     * n_zeilen_hinzu   == Anzahl Zeilen, für die neuer Speicherplatz
     * reserviert werden soll. Bei negativen Werten werden n Zeilen
     * entfernt
     * init_laenge      == Speicherplatz für die Länge jeder Zeile, die
     *                     angefordert wird
     * Rückgabewert: 1 wenn OK, ansonsten 0
     */
    
    int zeilen_hinzu_fuegen(char ***array_ptr, int alt_n_zeilen, int n_zeilen_hinzu, int init_laenge)
    {
        char **ptr;
        int ret = 1;
        int zeile;
        int anzahl_alte_zeilen = alt_n_zeilen;
    
        /* ein negativer Wert bedeutet Zeilen entfernen */
        if(n_zeilen_hinzu < 0)
        {
            for(zeile = anzahl_alte_zeilen-1; zeile >= anzahl_alte_zeilen + n_zeilen_hinzu; zeile--)
                free((*array_ptr)[zeile]);
        }
    
        /* Speicher für einzelne Zeilen reservieren */
        ptr = realloc(*array_ptr, (anzahl_alte_zeilen + n_zeilen_hinzu) * sizeof(**array_ptr));
    
        if(NULL != ptr)
        {
            *array_ptr = ptr;
    
            for(zeile = anzahl_alte_zeilen; ret && zeile < anzahl_alte_zeilen + n_zeilen_hinzu; zeile++)
            {
                /* Anzahl der Zeichen, die jede Zeile
                 * aufnehmen kann, reservieren */
                (*array_ptr)[zeile] = malloc(init_laenge);
    
                if(NULL != (*array_ptr)[zeile])
                    /* in jeder Zeile das erste Zeichen mit \0 initialisieren */
                    (*array_ptr)[zeile][0] = '\0';
    
                else
                    ret = 0;
            }
        }
    
        else
            ret = 0;
    
        return ret;
    }
    
    /*  **array_ptr == Ein Zeiger auf das String-Array
     *                 array[zeile][laenge]
     *      zeile_n == Anzahl Zeilen, die angepasst werden
     *  Rückgabewert bei Erfolg 0, ansonsten größer als 0
     */
    
    int string_anpassen(char **array_ptr, int zeile_n)
    {
        int zeile;
        int anzahl_zeichen;
        int fehlschlag = 0;
    
        for(zeile = 0; zeile < zeile_n; zeile++)
        {
            /* Funktion strlen liest das Terminierungszeichen
            * '\0' nicht mit -> daher +1 */
            anzahl_zeichen = strlen(array_ptr[zeile]) + 1;
    
            if(0 == string_groesse_veraendern(array_ptr, zeile, anzahl_zeichen))
                fehlschlag++;
        }
    
        return fehlschlag;
    }
    
    /* **array_ptr == Ein Zeiger (Adresse) auf das String-Array
     * array[zeile][laenge]
     *       zeile == Zeile, die verändert werden soll
     *  neu_laenge == Anzahl Zeichen, die für die Zeile verändert
     *                werden soll
     *  Rückgabewert bei Erfolg SUCCESS, ansonsten bei Fehler ERROR
     */
    
    int string_groesse_veraendern(char **array, int zeile, int neu_laenge)
    {
        char *ptr;
        int ret = SUCCESS;
    
        ptr = realloc(array[zeile], neu_laenge);
    
        if(ptr != NULL)
            array[zeile] = ptr;
    
        else
            ret = ERROR;
    
        return ret;
    }
    
    /* **array_ptr == Ein Zeiger (Adresse) auf das String-Array
     *                array[zeile][laenge]
     *   n_zeile   == Anzahl Zeilen, die freigegeben werden sollen */
    
    void free_string_array(char **array, int n_zeilen)
    {
        int zeile;
    
        if(array != NULL)
        {
            for(zeile = 0; zeile < n_zeilen; zeile++)
            {
                if(array[zeile] != NULL)
                    free(array[zeile]);
            }
        }
    
        free(array);
    }
    
    int main()
    {
        char **array = NULL;
        char datei[255];
        int zeilen = 0, i, auswahl, n, m;
    
        do
        {
            printf("\nWas wollen sie tun?\n\n");
            printf("-1- Datei komplett in den Speicher einlesen\n");
            printf("-2- Inhalt der Datei im Speicher ausgeben\n");
            printf("-3- Datei im Speicher von Zeile n bis m ausgeben\n");
            printf("-4- Den Speicher wieder freigeben\n");
            printf("-5- Ende\n\n");
            printf("Ihre Wahl : < >\b\b");
            scanf("%d", &auswahl);
            fflush(stdin);
    
            switch(auswahl)
            {
                case 1 : printf("Datei angeben (mit Pfadangabe): ");
                         scanf("%254s", datei);
                         fflush(stdin);
    
                         if((read_file(&array, datei, &zeilen)) == ERROR)
                            printf("Fehler beim Lesen in Speicher!!!\n");
    
                         break;
    
                case 2 : if(zeilen == 0)
                            printf("Keine Daten vorhanden!\n");
    
                         else
                            for(i = 0; i <= zeilen-1; i++)
                                printf("%s\n", array[i]);
    
                         break;
    
                case 3 : printf("Zeilenbeginn: ");
                         scanf("%d", &n);
                         fflush(stdin);
    
                         if(n > 0 && n <= zeilen)
                         {
                             printf("bis zur Zeile: ");
                             scanf("%d", &m);
                             fflush(stdin);
    
                             if(m >= n && m <= zeilen)
                             {
                                 for(i = n-1; i < m; i++)
                                    printf("%s\n", array[i]);
                             }
    
                             else
                                printf("??>>%d<<??\n", m);
                         }
    
                         else
                            printf("??>>%d<<??", n);
    
                         break;
    
                case 4 : free_string_array(array, zeilen);
                         zeilen = 0;
    
                         break;
    
                default: break;
            }
        } while(auswahl != 5);
    
        return EXIT_SUCCESS;
    }
    

    Sinn und Zweck dieses Programms ist es, den Text einer Datei dynamisch in den Speicher zu lesen.

    Im Grunde genommen funktioniert fast alles auch so wie es soll. Nur gibt es da ein Problem mit zwei Funktionen.

    Fangen wir mal mit der ersten Funktion an:

    int read_file(char ***, char *, int *);
    

    Da gibt es eine Stelle innerhalb der o.g. Funktion, in der eine andere Funktion aufgerufen wird, die praktisch die Stringlänge einer eingelesenen Zeile anpassen soll. D.h. wenn der String einer Zeile länger sein sollte als 255 Zeichen, wird anhand der u.g. Funktion die Länge der Zeile angepasst, in dem zusätzlich Speicher allokiert wird.

    /* kein Newline-Zeichen, dann Zeile länger als LINE
          * Zeichen, String der Länge anpassen -> Speicher
          * anfordern */
          else
          {
              absatz_n++;
    
              if(0 == string_groesse_veraendern(*array, zeile, absatz_n * LINE))
                  error = ERROR;
          }
    

    Die Funktion

    int string_groesse_veraendern(char **, int, int);
    

    sieht folgendermaßen aus:

    int string_groesse_veraendern(char **array, int zeile, int neu_laenge)
    {
        char *ptr;
        int ret = SUCCESS;
    
        ptr = realloc(array[zeile], neu_laenge);
    
        if(ptr != NULL)
            array[zeile] = ptr;
    
        else
            ret = ERROR;
    
        return ret;
    }
    

    So - nun habe ich zum testen eine Textdatei erstellt, die tatsächlich eine Zeile beinhaltet, dessen Länge die 255 Zeichen überschreitet.

    Der Versuch, diese Textdatei mit dem o.g. Programm zu öffnen scheitert mit der Fehlermeldung:

    Fehler beim Lesen in Speicher!!!
    

    Dies bedeutet - anstatt die Stringlänge im Speicher anzupassen, wird ERROR ausgegeben. Mein Problem ist, ich habe keine Ahnung warum.

    Habe mal so just for fun diese Zeile im Code:

    if(0 == string_groesse_veraendern(*array, zeile, absatz_n * LINE))
                  error = ERROR;
    

    folgendermaßen umgeändert:

    if(0 != string_groesse_veraendern(*array, zeile, absatz_n * LINE))
                  error = ERROR;
    

    So funktionierte es nach der Kompilierung. Allerdings bin ich mir der Tatsache bewusst, dass es aber so nicht vorgesehen ist.

    Daher bitte ich hier im Forum um Hilfe. Ich hoffe ihr könnt mir erklären wieso es nicht funktioniert.

    Ich verwende CodeBlocks unter Ubuntu 11.04 Natty - zur Kompilierung und Ausführung der Lisitings.

    Würde mich sehr über eure Hilfe freuen. 🙂

    Vielen Dank.

    Gruß

    Guest



  • 1. Du solltest dir ganz, ganz dringend Folgendes durchlesen: http://www.c-plusplus.net/forum/p2138807

    2. Mit deiner Änderung (Rückgabe von string_groesse_veraendern ) hast du völlig Recht. Das ist offensichtlich ein Fehler im Buch (was mich nicht überrascht).


  • Mod

    _matze schrieb:

    Das ist offensichtlich ein Fehler im Buch (was mich nicht überrascht).

    Siehe auch hier:
    http://www.c-plusplus.net/forum/272350



  • Ich empfehle dir, mit dem Autor nicht weiterzumachen, es bringt dir einfach nichts, dessen Fehler zu finden und zu verbessern.
    Wenn du den Code 1:1 so übernommen hast, spricht das nur wiederum für die Schizophrenie von Pfuscher JW, d.h. oben symbolische Konstanten einzuführen und sie dann nicht zu verwenden. Bei dir also müsste es dann

    if(SUCCESS != string_groesse_veraendern(*array, zeile, absatz_n * LINE))
                  error = ERROR;
    

    heißen, das ist dann leichter zu verstehen und Fehler sind leichter zu finden, wobei die Namensvergabe SUCCESS, ERROR, LINE, error auch wieder für die Praxisferne des pfuschenden Autors sprechen, solche kurzen allgemeinen Namen finden sich sehr leicht auch in irgendwelchen Headerdateien wieder und ziehen u.U. ärgerliche Debugorgien nach sich.
    Also wenn dann

    enum { MY_SUCCESS, MY_ERROR };
    

    oder entsprechend angepasst.



  • SeppJ schrieb:

    _matze schrieb:

    Das ist offensichtlich ein Fehler im Buch (was mich nicht überrascht).

    Siehe auch hier:
    http://www.c-plusplus.net/forum/272350

    Richtiger Thread, aber falsche Seite verlinkt. Upps. 😃


  • Mod

    _matze schrieb:

    SeppJ schrieb:

    _matze schrieb:

    Das ist offensichtlich ein Fehler im Buch (was mich nicht überrascht).

    Siehe auch hier:
    http://www.c-plusplus.net/forum/272350

    Richtiger Thread, aber falsche Seite verlinkt. Upps. 😃

    Eher upps von mir. Ich war deinem Link gar nicht gefolgt, ich hatte angenommen, dass der erste Link zu dem Thread über das richtige Stellen von Fragen geht, weil dies das erste war, was mir am Beitrag des Fragesteller aufgefallen ist.



  • Cool - erstmals vielen Dank für eure Antworten.

    Ja - ich bin ja auch noch ein ziemlicher Anfänger was die Programmierung angeht.
    Nun weiß ich das es mit

    if(0 != string_groesse_veraendern(*array, zeile, absatz_n * LINE))
                            error = ERROR;
    

    geht. Aber ich verstehe immer noch nicht warum.

    Das hier

    if(0 == zeilen_hinzu_fuegen(array, *zeile_n, ALLOC_LINE, LINE))
                                error = ERROR;
    

    funktioniert ja.

    Bevor ich angefangen hatte mich mit diesem Listing auseinander zu setzen, hatte ich das Gefühl schon einiges gelernt und verstanden zu haben. Doch nun verstehe ich einfach gar nix mehr.

    Könntet ihr mir noch etwas helfen, die Fehler die in diesem Listing gemacht worden sind zu korrigieren. Einfach um es besser zu verstehen.

    Dankeschön.

    Zum Buch selbst: Während der Durcharbeitung des Buches, sind mir auch schon einige Fehler aufgefallen. Doch auf der anderen Seite hatte ich auch sehr viele Aha-Effekte. Aber die Fehler speziell in diesem Listing verwirren mich schon sehr.

    Das Buch: C - Einführung und professionelle Anwendung habe ich auch. Ich lerne mit beiden Büchern. C von A bis Z und C - Einführung und ...

    Wie gesagt, würde mich freuen wenn ihr mir helfen könntet die Fehler im Listing zu korrigieren.

    Gruß
    Lancy78



  • Okay - alles klar. Habe es doch verstanden.
    Dann bedanke ich mich nochmal vielmals bei euch allen.

    Bis bald.

    Gruß Lancy78


Anmelden zum Antworten