Beliebig lange Zeichenkette
-
Ich habe eine Funktion zur Eingabe eines beliebig langen char - arrays geschrieben und die tut soweit auch was sie soll, ist aber wahrscheinlich weit davon entfernt, optimal zu sein.
char *leseString(void){ char *test, *folge; int i = 0; folge = malloc(sizeof(char)); if (folge == NULL){ printf("\nERROR"); free(folge); return NULL; } while (1){ folge[i] = getchar(); if (folge[i] == '\n'){ folge[i] = '\0'; return folge; } i++; test = realloc(folge, i + 1); if (test == NULL){ printf("\nERROR"); free(folge); return NULL; } } }
Was haltet ihr von der Funktion bzw. was kann ich daran verbessern?
Darf/Sollte ichwhile (1){...}
so schreiben (weil das ja ansich nicht terminiert)?
Vielen Dank im Voraus!
-
Brainstorming:
size_t für Größen.Fehlerbehandlung über printf gehört nicht in den Librarycode. Setz errno oder Ähnliches, wenn dir nichts besseres einfällt.
Was ist, wenn der Stream endet (EOF)?
Die realloc-Strategie könnte effizienter sein. Wenn du X*(alten Wert) statt (alten Wert) + 1 reservierst, dann brauchst du viel weniger Reallokationen (auf Kosten von ein wenig verschwendetem Speicher).
Den von realloc zurück gegebenen Wert verwirfst du, dein Programm ist also falsch. realloc verändert nicht sein erstes Argument (kann es auch gar nicht, da es by value übergeben wird).
Allgemein mag ich es nicht, wenn einfache Funktionen etwas zurück geben, was ge-free-t werden muss. Das muss entweder sehr gut dokumentiert werden oder die Daten für die Zeichenkette gehören objektorientiert gekapselt.
-
Ich habs jetzt mal so gelöst:
char *leseString2(void){ char *neu, *folge; int i = 0; int strsize = 1; folge = malloc(sizeof(char)); if (folge == NULL){ free(folge); return NULL; } while (1){ folge[i] = getchar(); if (folge[i] == EOF){ exit(EXIT_FAILURE); } if (folge[i] == '\n'){ folge[i] = '\0'; break; } i++; if (i == strsize){ neu = realloc(folge, strsize * 2); if (neu == NULL){ free(folge); return NULL; } strsize *= 2; folge = neu; } } return folge; }
Ist das so effizienter? Bei langen Eingaben dürfte der Speicherverbrauch doch recht groß werden...
Und kann man mit realloc den Speicherbereich auch wieder einengen bzw. nicht benutzten wieder freigeben?Die Funktion war auch eher nur zur Übung und nicht für irgendwelche Projekte, so weit bin ich noch lange nicht :p
-
Sieht schon besser aus
.
Was mich von der Benutzung der Funktion abhalten würde, wäre das exit. Kompletter Programmabbruch, wenn ein Dateiende erreicht wird? Das ist doch eigentlich der Moment, in dem ich anfangen will, die gelesenen Daten zu verarbeiten, also der Normalfall! Ich würde das gar nicht einmal als Fehler ansehen und eine Funktion hat grundsätzlich kein eigenständiges exit durchzuführen.
Der zu viel benutzte Speicher ist doch von der gleichen Größenordnung wie der notwendige Speicher. Das würde ich nicht so eng sehen.
Mit realloc kannst du reserviert Speicherbereiche auch verkleinern.
-
Vielleicht könnte man die Rückgabe anders lösen?
Wer gibt den Speicher wieder frei?
-
Bei EOF bin ich etwas ratlos, wie der Rückgabewert aussehen sollte, muss ja ein pointer sein, bzw. wie die anschließende "Fehler"-Behandlung aussehen soll...für eine kurze Erklärung/Anregungen wäre ich aber natürlich sehr dankbar
Man muss den Speicher leider selbst wieder (z.B. in der main Funktion) freigeben, mir fällt auch noch keine bessere Möglichkeit ein
-
KJoke schrieb:
Bei EOF bin ich etwas ratlos, wie der Rückgabewert aussehen sollte, muss ja ein pointer sein, bzw. wie die anschließende "Fehler"-Behandlung aussehen soll...für eine kurze Erklärung/Anregungen wäre ich aber natürlich sehr dankbar
Ich würde gar nichts machen. Eventuell, wenn gar nichts gelesen wurde und sofort EOF war, dann einen Nullzeiger. Die Standardfunktionen machen es auch so und das aus gutem Grund. Wieso willst du bei eof eine Fehlerbehandlung durchführen? Typische Leseschleifen in C sehen von der Logik her doch so aus (Prüfung von ferror lasse ich hier mal weg):
while (leseaktion(file, &daten) && !feof(file)) verarbeite(daten);
Du hast jetzt eine eigene Leseaktion definiert. Da die eof-Prüfung aber im Anwendungscode erfolgt, braucht und sollte deine Funktion sich darum nicht zu kümmern.
Man muss den Speicher leider selbst wieder (z.B. in der main Funktion) freigeben, mir fällt auch noch keine bessere Möglichkeit ein
struct um den String, Funktionen zum Anlegen und Zerstören dieses structs definieren. Nur deine Funktionen dürfen die Interna des structs anfassen. Dem Anwender bleibt nur die Pflicht, das struct vor Benutzung mit der Anlegefunktion zu initialisieren und nach Benutzung die Zerstörungsfunktion aufzurufen.
-
Wie sieht das eigentlich mit dem
EOF
in einemchar
aus. Wird das noch sicher erkannt?getchar
gibt ja nicht ohne Grund ein int zurück.Ändert sich der Wert von EOF wenn man beim Compiler den Typ von
char
von signed auf unsigned ändert?
-
DirkB schrieb:
Wie sieht das eigentlich mit dem
EOF
in einemchar
aus. Wird das noch sicher erkannt?Stimmt habe ich ganz übersehen. Das kann und wird schief gehen.
-
Aber bei EOF muss ich ja zumindest die lese-Funktion beenden. Und sollte ich im Rückgabewert nich irgendwie unterbringen, dass die Funktion wegen EOF beendet wurde? Und muss ich dann auch das letzte Zeichen (EOF oder das davor?)durch '\0' ersetzen?
Ich dachte char hat den Wertebereich -128 bis 127 und EOF ist mit -1 ja in diesem Wertebereich..?
Bei unsigned char hätte EOF den wert 255.
ansonsten eben das Zeichen erst als int Speichern ->auf EOF prüfen -> int wert in char schreiben
oder unsigned char == 255 prüfen (?)
-
KJoke schrieb:
Aber bei EOF muss ich ja zumindest die lese-Funktion beenden. Und sollte ich im Rückgabewert nich irgendwie unterbringen, dass die Funktion wegen EOF beendet wurde? Und muss ich dann auch das letzte Zeichen (EOF oder das davor?)durch '\0' ersetzen?
Sprich: Es sollte sich verhalten, als hättest du ein newline gefunden. Wieso sollte deine Funktion einen anderen Wert zurück geben, wenn sie EOF trifft. Wenn den Anwendungscode interessiert, ob EOF aufgetreten ist, dann muss er also den Rückgabewert deiner Funktion darauf prüfen. Da kann er aber einfacher ein feof auf den Stream machen.
Ich dachte char hat den Wertebereich -128 bis 127 und EOF ist mit -1 ja in diesem Wertebereich..?
Bei unsigned char hätte EOF den wert 255.Wie kommst du zu diesem Wissen? Das kann doch schon alleine deshalb nicht funktionieren, weil man dann einen Wert aus dem Wertebereich für chars hat. Kann man dann chars mit diesem Wert nie lesen?
ansonsten eben das Zeichen erst als int Speichern ->auf EOF prüfen -> int wert in char schreiben
oder unsigned char == 255 prüfen (?)Ja, so geht das.
-
getchar
liefert die Daten als positive Werte (o bis 255).
Daher kannst du dasEOF
auch von einem gültigen Zeichen unterscheiden.Erst die Zuweisung zu einem
char
machen sie dann signed (oder auch nicht).Daher erst das Resultat von
getchar
in einemint
ablegen, alle Besonderheiten abarbeiten und dann bei Bedarf im Array ablegen (und das Array evtl vergrößern).Wenn du realloc mit NULL im ersten Parameter aufrufst, verhält es sich wie malloc.
Es ist also nicht nötig vorher das malloc zu machen.char *leseString2(void){ char *neu, *folge; int c, size_t i = 0; size_t strsize = 1; folge = NULL; // Damit wäre der erste Aufruf von realloc schon ok. while (1){ c = getchar(); if (c == EOF){ ....; } // Hier dein realloc und Berechnung des Speicherbedarfs. } return folge; }