getline() wird vom compiler nicht erkannt



  • Hey Danke für den code!

    Folgende Fragen hätte ich trotzdem:

    1. Warum ist im schleifenkopf der for schleife nichts drin?

    2. Warum brauche ich 2 pointer s und p?

    3. Warum wird n%512==0 üperprüft, woher kommen die 512?

    4. c ist ein int wert, warum kommen da char's rein? (ich muss zahlen von einer datei einlesen, d.h. ich müsste mit atoi() und strtok() den string noch in zahlen umwandeln, aber jetzt wird in ein char array int-werte gespeicehrt, warum?



  • 5. wenn n größer als 512 wird, dann wird kein neuer speicher mehr alloziert, da n%512 dann ungleich 0 ist und irgendwann wenn n bei 1025 ist wird das "array" überschritten, oder nicht?



  • jay186 schrieb:

    1. Warum ist im schleifenkopf der for schleife nichts drin?

    Weil es so praktischer ist.

    Die Schleife läuft so lange bis '\n' eingegeben wird. Das in den Kopf zu packen würde die komplexität des Codes ein bisschen erhöhen. Deshalb nimmt er einfach eine "endlosschleife" und baut an der richtigen Stelle ein "break" ein um die Schleife zu beenden.

    2. Warum brauche ich 2 pointer s und p?

    In dem Code nicht. Erst wenn du einen Fehler bei realloc abfangen willst brauchst du 2 Zeiger. Den realloc würde dir s mit 0 überschreiben wenn es fehlschlägt und dein Speicher wäre weg.

    3. Warum wird n%512==0 üperprüft, woher kommen die 512?

    Frei erfunden. Es werden gerne 2er Potenzen genommen für Blockgrößen. Der Speicher in dieser Funktion wächst immer um 512 Bytes. Eine uU sinnvollere Strategie wäre ein *2 jedesmal. Im Prinzip ist die Zahl hier aber nur ein Schätzwert.

    4. c ist ein int wert, warum kommen da char's rein? (ich muss zahlen von einer datei einlesen, d.h. ich müsste mit atoi() und strtok() den string noch in zahlen umwandeln, aber jetzt wird in ein char array int-werte gespeicehrt, warum?

    Fehler im Code. EOF zeigt an wenn die Datei (oder in diesem Fall die Eingabe) zuende ist. zB wenn man eine Datei an das Programm piped. EOF kann aber kein char sein weil es nicht darstellbar ist. Deshalb ist EOF ein int und c muss daher ein int sein wenn man auf EOF testen will. Dieser Test fehlt im Code.

    5. wenn n größer als 512 wird, dann wird kein neuer speicher mehr alloziert, da n%512 dann ungleich 0 ist und irgendwann wenn n bei 1025 ist wird das "array" überschritten, oder nicht?

    n%512 ist 0 wenn n 0, 512, 1024,... ist. Jedesmal wenn das eintritt, wird das Array um 512 Elemente vergrößert.

    % liefert ja den Rest einer Division. Und alle vielfachen von 512 sind restlos durch 512 teilbar.

    Es fehlt in dem Code ein n+=512.



  • das hab ich mir mal überlegt:

    char *puffer = NULL;
    unsigned int counter = 0;
    size_t t = 1000;
    
    puffer = calloc(t, sizeof(char));
    
    while (true) {
         if (c = fgetc(FILE)) == "/n")
              break;
         else if (counter > "letzter Index des Arrays"){
              t=2*t;
              puffer = realloc(puffer,t);
              puffer[counter++] = c;}
         else puffer[counter++] = c;
    }
    


  • danke nochmals für die erläuterungen, werde wohl den vorgeschlagenen code nehmen!

    Noch ne Frage:

    was passiert wenn die letzte Zeile kein newline hat? was kann man da tun?



  • Es heisst '\n' und nicht "/n" und du hast EOF vergessen. Weiters hast du keine Fehlerüberprüfung wenn realloc/calloc fehlschlagen.

    EOF ist auch die Lösung des Problems wenn es kein \n gibt. fgetc liefert dir in so einem Fall EOF wenn er das Zeichen nach dem letzten liest.



  • okay danke! denke dass ich es jetzt hinbekommen.

    noch eine frage:

    was steht am Ende im "array"? integerwerte oder stringfolge?
    kann ich also strtok() auf das "array" anwenden?



  • jay186 schrieb:

    okay danke! denke dass ich es jetzt hinbekommen.

    noch eine frage:

    was steht am Ende im "array"? integerwerte oder stringfolge?
    kann ich also strtok() auf das "array" anwenden?

    Wenn es ein char* ist, stehen chars drinnen.

    In C ist alles ein string was aus chars besteht und am Ende den Wert '\0' stehen hat.



  • der int c wird also in ein char gecastet.
    woher kommt aber beim einlesen das "/0"?



  • jay186 schrieb:

    okay danke! denke dass ich es jetzt hinbekommen.

    noch eine frage:

    was steht am Ende im "array"? integerwerte oder stringfolge?
    kann ich also strtok() auf das "array" anwenden?

    Am Ende eines Strings Arrays muss eine 0 (das \0-Zeichen) stehen, sonst ist es keine C-String und die Funktionen von string.h liefern ein undefiniertes Verhalten.

    edit: uups, ein bisschen zu spät 😡

    jay186 schrieb:

    der int c wird also in ein char gecastet.
    woher kommt aber beim einlesen das "/0"?

    nur die erten 8 Bits kommen rein, bei normalen Zeichen (Buchstaben) kein Problem. Das \0 Zeichen musst du selber reintun, wenn du Zeichen für Zeichen liest. Und es heißt \0, \n, usw, mit einem Backslash.

    Hier mein Code 😉 (Achtung, ungetestet)

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    void *inc_memory(void *ptr, size_t *act_size, size_t new_size)
    {
        if(*act_size < 2*new_size + 1)
        {
            /* no data loss if realloc returns NULL */
            ptr = realloc(ptr, 2*new_size+1); 
    
            if(ptr == NULL)
                return NULL;
    
            *act_size = 2*new_size+1;
        }                
    
        return ptr;      
    }                    
    
    char *get_next_line(char *buff, FILE *fp)
    {
        char *tmp;
        int c;
        size_t len = 0, new_len = 0, pos = 0;
    
        while( ((c=fgetc(fp)) != '\n') && ( c != EOF ) && !feof(fp))
        {
            new_len++;
            tmp = inc_memory(buff, &len, new_len);
            if(tmp == NULL)
            {
                printf("inc_memory returns NULL\n");
                free(buff);
                return NULL;
            }
    
            buff = tmp;
            buff[pos] = c;
            buff[++pos] = 0;
        }                               
    
        return buff;
    }
    
    int main(void)                      
    {
        char *line = NULL;
    
        line = get_next_line(line, stdin);
    
        if(line == NULL)
        {
            fprintf(stderr, "Shit\n");
            return 1;
        }
    
        printf("Eingabe: \"%s\"\n", line);
    
        free(line);
    
        return 0;
    }
    


    /* no data loss if realloc returns NULL */
            ptr = realloc(ptr, 2*new_size+1); 
    
            if(ptr == NULL)
                return NULL;
    

    wo wird hier ptr gesichert?

    2. was passiert hier:

    !feof(fp)
    

    und

    3. warum

    if(*act_size < 2*new_size + 1)
    

    2*x+1?



  • benötigt man die #include <string.h>?
    ich darf nur die c Standardbibltiothek nutzen...



  • Die string.h gehört zur Standardbibliothek.



  • void *inc_memory(void *ptr, size_t *act_size, size_t new_size)
    

    Warum wird size_t new_size nicht per call-by-reference üebrgeben, size_t *act_size aber schon?



  • Shade Of Mine schrieb:

    Es fehlt in dem Code ein n+=512.

    brauch nicht, die zeile s[n++] = c; macht das.
    🙂



  • @ supertux

    wie ist die abbruchbedingung wenn ich alle zeilen einlesen möchte?

    while (get_next_line(line, datei) ???)
    


  • Undertaker schrieb:

    Shade Of Mine schrieb:

    Es fehlt in dem Code ein n+=512.

    brauch nicht, die zeile s[n++] = c; macht das.
    🙂

    stimmt, sorry. habe da etwas verwechselt.



  • Zu Frage 1:

    Ich verliere keinen Pointer, weil ich nur ein Kopie verwalte. Vergleiche folgende Codeblöcke

    void foo(void)
    {
        void *something;
        size_t many = 8991, lots=999;
        something = malloc(many);
        if(something == NULL) return;
    
        something = realloc(something, many + lots);
        ...
    }
    

    Hier kann ich den zuvor Reservierten Speicher verlieren, wenn realloc fehlschlägt und NULL zurückliefert, was heißt, dass die neue Speichermenge nicht reserviert werden konnte, aber 'something' an sich nicht geändert wurde noch freigegeben wurde. Deswegen ist es ratsmal, eine Temporäre Variable zu benutzen.

    void *something, *tmp;
    ...
    tmp = realloc(something, many + lots);
    
    if(tmp==NULL)
    {
        fprintf(stderr, "ALARM!!!!!!!\n");
        free(something);
        return;
    }
    
    something = tmp;
    

    Dadurch kann ich im Falle, dass realloc die neue Speichermenge nicht allozieren kann, den alten Pointer noch retten.

    Betrachte nun mal folgenden Code:

    void *bar(void *something, size_t newlen)
    {
        something = realloc(something, newlen);
    
        if(something == NULL) return NULL;
    
        /* irgendwas mit something machen */
    
        return something;
    }
    

    Achter hier darauf, dass something dieses Mal nur eine Kopie des echten Pointers ist. Somit, wenn diese Kopie verloren geht, macht ja nichts, denn die Funktion, die bar aufruft, das Original hat. Es ist also die Aufgabe vom Aufrufer von bar, dafür zu sorgen, wie bei realloc, das Original zu sicher.

    void foobar(void)
    {
        void *original, *tmp;
    
        original = malloc(...);
    
        ...
    
        tmp = bar(original, ...);
    
        if(tmp == NULL) { free(original); return;}
    
        original = tmp;
    }
    

    Ich habe also die Funktion inc_memory ähnlich wie realloc implementiert, genaugenommen ist diese Funktion nur ein "intelligenterer" Wrapper von realloc, der anhand der aktuellen reservierten Menge und der neuen verlangten Menge die "richtige" Menge reserviert. So kann ich dort bestimmte Heuristiken implementieren, die festlegen, wieviel Speicher reserviert werden muss, bei mir z.B. ist die Heuristik: wenn weniger als die Hälfte frei ist, dann verdoppelte die angefordete Menge. Ob diese Heuristik effizient ist, weiß ich nicht, es war nur ein Beispiel für dich.

    Zu Frage 3: siehe 1.

    Warum wird size_t new_size nicht per call-by-reference üebrgeben, size_t *act_size aber schon?
    

    Siehe 1. Wegen meiner Heuristik muss ich wissen, welche die neue Länge ist. Da man mit C nur einen einzigen Wert per return liefern kann, muss ich act_size (actual size) als Zeiger übergeben, damit diese auf die neue aktuelle Länge verändert wird.

    Zu Frage 2 (und zu "wie ist die abbruchbedingung wenn ich alle zeilen einlesen möchte?" Frage):

    feof guckt nach, ob EOF an einem FILE buffer erreicht wurde, sprich, ob die Datei zu Ende gelesen wurde. Bei stdin kaum möglich, aber meine get_next_line Routine liest die nächste Zeile aus generischen FILE Puffer, insbesondere stdin, wenn z.B. der Inhalt durch eine Pipe kommt.

    Da fällt mir aber ein, dass meine Routine NULL zurückliefert, wenn man eine empty Line liest, sprich nur eine Zeile mit \n als erstes Zeichen. Eine kleine Anpassung ist nötig, da kannst du sowas machen:

    char *get_next_line(char *buff, FILE *fp)
    {
        char *tmp;
        int c;
        size_t len = 0, new_len = 0, pos = 0;
    
        while( ((c=fgetc(fp)) != '\n') && ( c != EOF ) && !feof(fp))
        {
            new_len++;
            tmp = inc_memory(buff, &len, new_len);
            if(tmp == NULL)
            {
                printf("inc_memory returns NULL\n");
                free(buff);
                return NULL;
            }
    
            buff = tmp;
            buff[pos] = c;
            buff[++pos] = 0;
        }
    
        if(len == 0 && !feof(fp))
        {
            /* empty line */
            tmp = inc_memory(buff, &len, 1);
            if(tmp == NULL)
            {
                free(buff);
                return NULL;
            }
            buff = tmp;
            *buff = 0;
        }
    
        return buff;
    }
    
    int main(int argc, char **argv)
    {
        int ln = 0;
        char *line = NULL;
        FILE *fp;
    
        if(argc == 2)
        {
            fp = fopen(argv[1], "r");
            if(fp == NULL)
            {
                fprintf(stderr, "Cannot open %s\n", argv[1]);
                return 1;
            }
        } else
            fp = stdin; /* keine File angabe, dann Daten über stdin lesen */
    
        while(line = get_next_line(line, fp))
        {
            printf("%0.4d: %s\n", ++ln, line);
            free(line);
            line = NULL; /* da realloc(NULL, size)) === malloc(size) */
        }
    
        fclose(fp);
    
        return 0;
    }
    

    Das liest die gesamte Datei ein und gibt dir Zeile für Zeile jede Zeile wieder aus.

    supertux@supertux:~> cat abc.txt 
    abc
    def
    nächste Zeile ist leer
    
    xyz
    supertux@supertux:~> ./atest abc.txt 
    0001: abc
    0002: def
    0003: nächste Zeile ist leer
    0004: 
    0005: xyz
    supertux@supertux:~> cat abc.txt | ./atest 
    0001: abc
    0002: def
    0003: nächste Zeile ist leer
    0004: 
    0005: xyz
    supertux@supertux:~> ./atest 
    Test 1
    0001: Test 1
    Test2
    0002: Test2
    <Strg+D> gedrückt, bei Windows <Strg+Z> glaub ich ;)
    supertux@supertux:~>
    

    Wie gesagt, meine Heuristik ist vielleicht nicht effizient, ich würde selber sie so nicht einsetzen, aber als Beispiel für diese Erklärung ist sie auf jeden Fall gut.



  • Danke vielmals, hat mir extrem weitergeholfen!



  • supertux schrieb:

    Zu Frage 3: siehe 1.

    Warum wird size_t new_size nicht per call-by-reference üebrgeben, size_t *act_size aber schon?
    

    Siehe 1. Wegen meiner Heuristik muss ich wissen, welche die neue Länge ist. Da man mit C nur einen einzigen Wert per return liefern kann, muss ich act_size (actual size) als Zeiger übergeben, damit diese auf die neue aktuelle Länge verändert wird.

    Also diesen Punkt habe ich noch nicht verstanden, man kann doch auch zeiger dereferenzieren, dann hat man den wert...


Anmelden zum Antworten