realloc = free+malloc?
-
Hallöle,
ich bin grad dabei, wieder in C einzusteigen und stolpere leider noch mit realloc herum. Zum herumspielen hab ich eine Funktion geschrieben, die den Inhalt einer Textdatei ausliest und will sie nun vereinfachen:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { FILE *fhandle = fopen("sample.txt", "r"); char* text_buffer; char* line=(char*)malloc(512); char* text=""; int length = 0; while (fgets(line, sizeof(char)*512, fhandle) != NULL && !feof(fhandle)) { length = strlen(text); text_buffer = (char*)malloc(length+strlen(line)); strcpy(text_buffer, text); /* kopiere alten text */ strcpy(text_buffer+length, line); /* hänge neue zeile an */ free(text); text = NULL; text = (char*)malloc(strlen(text_buffer)); strcpy(text, text_buffer); free(text_buffer); text_buffer = NULL; } fclose(fhandle); printf("%s", text); free(text); text = NULL; free(line); line = NULL; free(text_buffer);text_buffer = NULL; return EXIT_SUCCESS; }
Soweit funktioniert das Programm, aber wenn ich nun z.B. Zeile 16+17 durch folgendes ersetze, funktioniert gar nix mehr:
text = (char*)realloc(text, strlen(text_buffer));
oder die ganze schleife ersetzt durch:
while (fgets(line, sizeof(char)*512, fhandle) != NULL && !feof(fhandle)) { length = strlen(text); text = (char*)realloc(text, length+strlen(line)); strcpy(text+length, line); }
Dann reagiert das Programm nicht mehr (gcc) oder stürtzt ab (lcc).
Compiler: aktuellster MinGW, LCC-Win64
Betriebssystem: Windows 7Gruß, Micha
-
Du hast versucht, "" zu reallocen.
Nee.char* text=malloc(1);*txt=0;
oben hin. Oder was Hübscheres, wie nicht in der Schleife immer strlen(text) zu machen.
-
und bei malloc/realloc den Platz für den Terminator nicht vergessen.
-
Danke euch beiden!
Der fehlende Platz für den Terminator hat mich eben fast zu Verzweiflung gebracht, als keine Programmversion mehr wollte.. danke für den Hinweis!volkard schrieb:
Oder was Hübscheres, wie nicht in der Schleife immer strlen(text) zu machen.
Was meinst du damit? strlen(text) muss doch in der Schleife stehen, weil es bei jedem Schleifendurchlauf eine andere Länge sein kann?
Der verbesserte Quellcode:
(weitere Verbesserungsvorschläge sind herzlich willkommen!)#include <stdio.h> #include <stdlib.h> #include <string.h> int main(void) { FILE *fhandle = fopen("sample.txt", "r"); char* line = (char*)malloc(512); char* text = (char*)malloc(1); if (line == NULL || text == NULL) { fprintf(stderr, "Kann keinen Speicher allozieren"); exit(EXIT_FAILURE); } *text = 0; int length = 0; while (fgets(line, sizeof(char)*512, fhandle) != NULL && !feof(fhandle)) { length = strlen(text); text = (char*)realloc(text, length+strlen(line)+1); if (text == NULL) { fprintf(stderr, "Kann keinen Speicher allozieren"); exit(EXIT_FAILURE); } strcpy(text+length, line); } fclose(fhandle); printf("%s", text); free(text); text = NULL; free(line); line = NULL; return EXIT_SUCCESS; }
-
Kümmelkorn schrieb:
volkard schrieb:
Oder was Hübscheres, wie nicht in der Schleife immer strlen(text) zu machen.
Was meinst du damit? strlen(text) muss doch in der Schleife stehen, weil es bei jedem Schleifendurchlauf eine andere Länge sein kann?
Nö. length kannste auch mitführen und hochzählen lassen.
-
du kannst das ganze einfacher lösen indem du mithilfe von fseek und ftell die größe der datei bestimmst.(funktioniert nur im b modus)
Lass die casts bei void* weg, mit dem cast nimmst du dir die typprüfung.
und du konntest auch zB sizeof(*line) anstatt sizeof(char) nehmen, dann kannst du den typ später ändern und musst dann nicht alles absuchen.
-
gary1195 schrieb:
Lass die casts bei void* weg, mit dem cast nimmst du dir die typprüfung.
D.h. bei folgendem Quellcode ist die If-Abfrage nie true? Oder wie meinst du das mit der Typprüfung?
char* foo = (char*)malloc(1); if(foo==NULL) { ... }
gary1195 schrieb:
du konntest auch zB sizeof(*line) anstatt sizeof(char) nehmen
Gute Idee
Hat sich aber glaub im neuen Code durch die Variable "buffer" erledigt.
volkard schrieb:
Nö. length kannste auch mitführen und hochzählen lassen.
Bist du dir sicher? Wenn ich die length wie sie jetzt drinsteht in der Schleife ausgeben lassen, erhalte ich mit meiner Beispieldatei folgende Werte:
0 24 60 84 133 134Ich muss also davon ausgehen, dass fgets nicht immer bis zur Puffergröße ließt, sondern ggf. nur bis zum Zeilenende \n, d.h. die Rückgaben von fgets haben unterschiedliche Längen, wenn die Zeilen in der Textdatei nicht alle gleich lang sind. Da erschließt sich mir jetzt kein mathematischer Zusammenhang, mit dessen Hilfe ich length einfach hochzählen kann!?
du kannst das ganze einfacher lösen indem du mithilfe von fseek und ftell die größe der datei bestimmst.(funktioniert nur im b modus)
Danke für den Tipp, werd ich heute mal probieren
Aktueller Quellcode:
#include <stdio.h> #include <stdlib.h> #include <string.h> char* read_file(const char *const filename, const size_t buffer) { FILE *fhandle = fopen(filename, "r"); char* line = malloc(buffer); char* text = malloc(1); int length = *text = 0; if (line == NULL || text == NULL) { fprintf( stderr, "Can't allocate enough memory. Do you have enough RAM?"); exit(EXIT_FAILURE); } do { line = fgets(line, buffer, fhandle); length = strlen(text); printf("%d ", length); text = realloc(text, (length+strlen(line)+1)); if (text == NULL) { fprintf( stderr, "Can't allocate enough memory. Do you have enough RAM?"); exit(EXIT_FAILURE); } strcpy(text+length, line); } while(line != NULL && !feof(fhandle)); fclose(fhandle); free(line); line = NULL; return text; } int main(void) { char* text = read_file("sample.txt", 64); printf("%s", text); free(text); text = NULL; return EXIT_SUCCESS; }
-
Kümmelkorn schrieb:
gary1195 schrieb:
Lass die casts bei void* weg, mit dem cast nimmst du dir die typprüfung.
D.h. bei folgendem Quellcode ist die If-Abfrage nie true? Oder wie meinst du das mit der Typprüfung?
char* foo = (char*)malloc(1); if(foo==NULL) { ... }
das hat nichts mit der if abfrage zu tun
folgendes//wenn dies ohne fehler compiliert //hat foo den rückgabetyp int *a = foo(); // int* oder void* int *a = (int*)foo(); // je nach compiler: irgendein pointertyp oder irgendein typ
durch den cast werden die typchecks des compilers umgangen
nach meiner erfahrung (C-kurs an uni) kann dieses verhalten sehr schnell zu "casteritis" führen
damit meine ich das alles gecastet wird (weil C ja so typunsicher ist(deshalb stürzt das programm ja ab)) (du scheinst nicht erkrankt zu sein)
-
Kümmelkorn schrieb:
volkard schrieb:
Nö. length kannste auch mitführen und hochzählen lassen.
Bist du dir sicher? Wenn ich die length wie sie jetzt drinsteht in der Schleife ausgeben lassen, erhalte ich mit meiner Beispieldatei folgende Werte:
0 24 60 84 133 134Gemeint ist nicht das sizeof(line), das sicherlich nach jedem fgets einmal durchgeführt werden muss.
sizeof(text) ist allerdings überflüssig, da du diese Länge bereits zuvor berechnen musstest, um überhaupt genügend Speicher reservieren zu können.
-
camper schrieb:
sizeof(text) ist allerdings überflüssig, da du diese Länge bereits zuvor berechnen musstest
text wird ja bei jedem Schleifendurchlauf neu realloziert, sodass sich dessen Größe ständig ändert. Das ist ja der Grund, warum ich die Funktion zum Datei einlesen so kompliziert schreibe - damit ich den Speicher dynamisch erweitern kann und die Größe nicht im Vorfeld festlegen muss. In Google findet man ständig Beispiele zum Dateien einlesen, in denen vorher festgelegt wird, wieviel Stellen eingelesen werden. Das wollte ich dynamisch und flexibel gestalten
Allerdings werd ich bei Gelegenheit der Idee von gery nachgehen und die Größe der Datei einfach vorher auslesen. Sollte wohl wirklich einiges vereinfachen *unschuldigpfeif*
vorläufig finale version: (danke nochmal an alle geduldigen Helfer
)
#include <stdio.h> #include <stdlib.h> #include <string.h> typedef struct { char* path; /* path to file */ char* content; /* content of the file */ } file; void file_init(file *this, const char *const path) { this->content = malloc(1); this->path = malloc(strlen(path)); if (this->content == NULL || this->path == NULL) { fprintf(stderr, "Can't allocate enough memory. Do you have enough RAM?"); exit(EXIT_FAILURE); } strcpy(this->path, path); *(this->content) = 0; } char* file_read(file *this, const size_t buffer_size) { FILE *fhandle = fopen(this->path, "r"); int length = 0; char* buffer = malloc(buffer_size); if (buffer == NULL) { fprintf( stderr, "Can't allocate enough memory. Do you have enough RAM?"); exit(EXIT_FAILURE); } do { buffer = fgets(buffer, buffer_size, fhandle); length = strlen(this->content); this->content = realloc(this->content, (length+strlen(buffer)+1)); if (this->content == NULL) { fprintf( stderr, "Can't allocate enough memory. Do you have enough RAM?"); exit(EXIT_FAILURE); } strcpy((this->content)+length, buffer); } while(buffer != NULL && !feof(fhandle)); fclose(fhandle); free(buffer); buffer = NULL; return this->content; } void file_free(file *this) { free(this->content); free(this->path); this->content=NULL; this->path=NULL; } int main(void) { file f; file_init(&f, "sample.txt"); file_read(&f, 64); printf("%s", f.content); file_free(&f); return 0; }
-
this->path = malloc(strlen(path) +1); //Nullbyte nicht vergessen