getline() wird vom compiler nicht erkannt
-
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...
-
Guck, meine Heuristik verlangt folgende Daten:
- die aktuelle Anzahl von Bytes, die reserviert wurden (act_size)
- die Anzahl von angeforderten Bytes (new_size)
Dabei kann es sein, dass act_size sich ändert, wenn realloc neuen Speicher reserviert. Ich muss mir dann die neue aktuelle Anzahl von reservierten Bytes irgendwie merken. Als Rückgabe Wert der Funktion kann ich sie nicht benutzen, denn die Funktion liefert bereits einen anderen Wert, nämlich einen Zeiger auf den neuen Speicher. Damit die Funktion act_size aktualisieren kann, muss ich act_size als Pointer übergeben oder was du als "call-by-reference" bezeichnest [*].
[*] in C gibt es keine Referenzen und keine "call-by-reference". Bitte Undertaker, keine weitere Disskusionen deswegen anfangen
-
supertux schrieb:
[*] in C gibt es keine Referenzen und keine "call-by-reference". Bitte Undertaker, keine weitere Disskusionen deswegen anfangen
keine angst.
ob du mist erzählst oder nicht, geht mich doch nichts an.
-
supertux schrieb:
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.
Ok, das mit dem Zeiger hab ich verstanden, nun habe ich noch folgende Fragen:
Zeile 10: inc_memory gibt doch buff zurück, dann wird also tmp = buff gesetzt, zeigt tmp also auf den erhöhten speicherbereich buff.
Warum wird dann nochmal
buff=tmp
gesetzt? die sind doch schon wegen Zeile 10 gleich, oder?
2. Warum steht nirgends free(tmp)?
3.wenn ich folgendes tue:
char *array; array = get_next_line(array,datei); free(array); array = NULL;
Reicht das um den reservierten Speicher wieder freizugeben oder könnten durch Frage 2. z.B. Speicherlecks entstehen?
4. Wo ist der unterschied zwischen den argumenten:
( c != EOF ) && !feof(fp))
5. der teil nach /*empty line*/ sorgt ja dafür, dass bei einer leeren Zeile nicht abgebrochen wird, jedoch wird durch
*buff = 0;
wieder bei return buff, NULL zurückgegeben, wo ist also der unterschied?
Sind viele Fragen ich weiß, ich versuchs halt zu verstehen...
-
jay186 schrieb:
Zeile 10: inc_memory gibt doch buff zurück, dann wird also tmp = buff gesetzt, zeigt tmp also auf den erhöhten speicherbereich buff.
Warum wird dann nochmal
buff=tmp
gesetzt? die sind doch schon wegen Zeile 10 gleich, oder?
Sicherheitsgründe. Wenn inc_memory fehlschlägt weil kein Speicher mehr da ist und wir das Ergebnis von inc_memory direkt an buff zugewiesen hätten, hätten wir ja alten Speicher verloren, da wir keinen zeiger mehr darauf haben.
Wir weisen also zuerst an tmpl zu und schauen ob alles geklappt hat - wenn etwas schief ging haben wir immer noch unseren alten Speicher den wir benutzen können (zB für datensicherung).
2. Warum steht nirgends free(tmp)?
Weil tmp nie lange auf seinen eigenen Speicher zeigt. Wenn inc_memory erfolgreich war, weisen wir tmp ja immer buff zu. So dass buff und tmp auf den neuen Speicherbereich zeigen. Es reicht dann buff zu free'en.
3.wenn ich folgendes tue:
char *array; array = get_next_line(array,datei); free(array); array = NULL;
Reicht das um den reservierten Speicher wieder freizugeben oder könnten durch Frage 2. z.B. Speicherlecks entstehen?
Das klappt nicht, da array auf ordentlichen Speicher zeigen muss. Bei dir zeigt array aber irgendwo in den Speicher und der gehört meistens nicht deinem Programm. Und moderne Systeme melden dann eine "Access Violation" wenn du in Speicher schreiben willst der nicht dir gehört.
Aber im Prinzip wenn array einen guten startwert hätte, wäre dein klein Code korrekt.
4. Wo ist der unterschied zwischen den argumenten:
( c != EOF ) && !feof(fp))
!feof(fp) ist hier redundant. c!=EOF fängt bereits das EOF ab.
5. der teil nach /*empty line*/ sorgt ja dafür, dass bei einer leeren Zeile nicht abgebrochen wird, jedoch wird durch
*buff = 0;
wieder bei return buff, NULL zurückgegeben, wo ist also der unterschied?
Achtung: buff=0 und *buff=0 sind 2 unterschiedliche Sachen.
*buff=0 ist eine andere Schreibweise für buff[0]='\0';
Und setzt damit lediglich das erste Zeichen des Strings auf den buff zeigt auf 0. Durch 0 innerhalb eines strings wird das Ende angezeigt. Es setzt also die länge des Strings auf 0.Sind viele Fragen ich weiß, ich versuchs halt zu verstehen...
Wer nix fragt, der lernt nix. Also gogogogogo mehr fragen
-
ok denke nun alles verstanden zu haben, werde nun alles schildern wie ich meine es verstanden zu haben. Folgender Code:
char *array = NULL; array = get_next_line(array,datei); /*mach was mit array*/ free(array); array = NULL;
Also, durch
array = get_next_line(array,datei);
wird eine Kopie des Zeigers "array" an get_next_line übergeben.
Dann wird mit
tmp = inc_memory(buff, &len, new_len);
versucht den Speicherbereich auf den "array" und auch die Kopie "buff" (welche wiederrum als Kopie übergeben wird?) zeigt (sollte derselbe sein) zu vergrößern und tmp zuzuwiesen.
Schlägt dieser versuch fehl ist tmp=NULL, aber "array" zeigt noch auf den alten speicherbereich und ich bekomme keine speicherlecks, da ich noch
free(array) oder free(buff) aufrufen kann.
Wird jedoch der Speicherbereich erfolgreich vergößert zeigt tmp auf den neuen Speicherbereich und buff noch auf den alten. Nun weise ich buff = tmp zu, damit buff auf den neuen Speicherbereich zeigt (und der alte? Speicherleck?).Nun zeigt "array" auf den neuen Speicherbereich.
und mitfree(array); array = NULL;
kann ich den wieder freigeben, so dass keine (?) Speicherlecks entstehen.
-
jay186 schrieb:
Wird jedoch der Speicherbereich erfolgreich vergößert zeigt tmp auf den neuen Speicherbereich und buff noch auf den alten. Nun weise ich buff = tmp zu, damit buff auf den neuen Speicherbereich zeigt (und der alte? Speicherleck?).
es gibt keinen Speicherleck. Es gibt 2 Möglichkeiten: entweder kann realloc die Speichermenge verändern, ohne den Speicher irgendwo anders verschieben zu müssen, oder realloc muss den Speicher irgendwo anders reservieren und liefert einen anderen Zeiger zurück. In diesem Fall gibt realloc den alten Speicher von alleine frei.
Ich mache sowas wie
array = get_next_line(array,datei);
ohne tmp = get_next_line(array....) nur deswegen, weil ich weiß, dass array ein NULL Pointer ist und in diesem Falle get_next_line den Speicher automatisch reserviert. Und wenn get_next_line NULL zurückliefern würde, spielt da keine Rolle, dass array mit NULL überschrieben wird, da der alte Wert von array NULL war.
So wie du es beschrieben hast, scheint es mir, dass du es langsam begreifst. Aber mach dich nicht verrückt, ob buff eine Kopie von array ist oder nicht, denn auf den Inhalt kommt's an, sprich worauf sie zeigen und nicht wer sie sind.
-
alles klar!
das sollte nun reichen...