Ich habe diese Funktion geschrieben, um eine Zeile aus einer Datei zu lesen:
const char *readLine(FILE *file) {
if (file == NULL) {
printf("Error: file pointer is null.");
exit(1);
}
int maximumLineLength = 128;
char *lineBuffer = (char *)malloc(sizeof(char) * maximumLineLength);
if (lineBuffer == NULL) {
printf("Error allocating memory for line buffer.");
exit(1);
}
char ch = getc(file);
int count = 0;
while ((ch != '\n') && (ch != EOF)) {
if (count == maximumLineLength) {
maximumLineLength += 128;
lineBuffer = realloc(lineBuffer, maximumLineLength);
if (lineBuffer == NULL) {
printf("Error reallocating space for line buffer.");
exit(1);
}
}
lineBuffer[count] = ch;
count++;
ch = getc(file);
}
lineBuffer[count] = '\0';
char line[count + 1];
strncpy(line, lineBuffer, (count + 1));
free(lineBuffer);
const char *constLine = line;
return constLine;
}
Die Funktion liest die Datei korrekt, und mit printf sehe ich, dass die Zeichenkette constLine auch korrekt gelesen wurde.
Wenn ich die Funktion jedoch z.B. so verwende:
while (!feof(myFile)) {
const char *line = readLine(myFile);
printf("%s\n", line);
}
gibt printf Kauderwelsch aus. Warum?
In Ihrer Funktion readLine
geben Sie einen Zeiger auf das Array line
zurück (streng genommen einen Zeiger auf das erste Zeichen, aber der Unterschied ist hier irrelevant). Da es sich um eine automatische Variable handelt (d.h. sie befindet sich "auf dem Stack"), wird der Speicher wieder freigegeben, wenn die Funktion zurückkehrt. Sie sehen Kauderwelsch, weil printf
sein eigenes Zeug auf den Stack gelegt hat.
Sie müssen einen dynamisch zugewiesenen Puffer von der Funktion zurückgeben. Sie haben bereits einen, es ist lineBuffer
; alles, was Sie tun müssen, ist, ihn auf die gewünschte Länge abzuschneiden.
lineBuffer[count] = '\0';
realloc(lineBuffer, count + 1);
return lineBuffer;
}
ADDED (Antwort auf die Folgefrage im Kommentar): readLine
gibt einen Zeiger auf die Zeichen zurück, aus denen die Zeile besteht. Dieser Zeiger ist das, was Sie brauchen, um mit dem Inhalt der Zeile zu arbeiten. Er ist auch das, was Sie an free
übergeben müssen, wenn Sie den von diesen Zeichen belegten Speicher nicht mehr benötigen. Hier sehen Sie, wie Sie die Funktion readLine
verwenden können:
char *line = readLine(file);
printf("LOG: read a line: %s\n", line);
if (strchr(line, 'a')) { puts("The line contains an a"); }
/* etc. */
free(line);
/* After this point, the memory allocated for the line has been reclaimed.
You can't use the value of `line` again (though you can assign a new value
to the `line` variable if you want). */
readLine()
gibt einen Zeiger auf eine lokale Variable zurück, was zu undefiniertem Verhalten führt.
Um dies zu umgehen, können Sie:
readLine()
übergebenline
mit malloc()
zuweisen - in diesem Fall ist line
persistentconst char *readLine(FILE *file, char* line) {
if (file == NULL) {
printf("Error: file pointer is null.");
exit(1);
}
int maximumLineLength = 128;
char *lineBuffer = (char *)malloc(sizeof(char) * maximumLineLength);
if (lineBuffer == NULL) {
printf("Error allocating memory for line buffer.");
exit(1);
}
char ch = getc(file);
int count = 0;
while ((ch != '\n') && (ch != EOF)) {
if (count == maximumLineLength) {
maximumLineLength += 128;
lineBuffer = realloc(lineBuffer, maximumLineLength);
if (lineBuffer == NULL) {
printf("Error reallocating space for line buffer.");
exit(1);
}
}
lineBuffer[count] = ch;
count++;
ch = getc(file);
}
lineBuffer[count] = '\0';
char line[count + 1];
strncpy(line, lineBuffer, (count + 1));
free(lineBuffer);
return line;
}
char linebuffer[256];
while (!feof(myFile)) {
const char *line = readLine(myFile, linebuffer);
printf("%s\n", line);
}
Beachten Sie, dass die Variable 'line' in der aufrufenden Funktion deklariert und dann übergeben wird, so dass Ihre Funktion readLine
den vordefinierten Puffer füllt und ihn einfach zurückgibt. Dies ist die Art und Weise, wie die meisten C-Bibliotheken arbeiten.
Es gibt andere Wege, die mir bekannt sind:
char line[]
als statisch
(static char line[MAX_LINE_LENGTH]
-> sie behält ihren Wert NACH der Rückkehr aus der Funktion). -> schlecht,
die Funktion ist nicht reentrant, und
Race Condition kann auftreten -> wenn Sie
sie zweimal von zwei Threads aus aufruft, wird sie
wird sie ihre Ergebnisse überschreibenmalloc()
, das die char line[], und
sie in den aufrufenden Funktionen wieder freigeben ->
zu viele teure malloc
s, und,
die Verantwortung für die Freigabe des Puffers an eine andere Funktion zu delegieren (die eleganteste Lösung besteht darin, malloc
und free
für alle Puffer in derselben Funktion aufzurufen)btw, 'explizites' Casting von char*
nach const char*
ist überflüssig.
btw2, es besteht keine Notwendigkeit, den lineBuffer zu malloc()
, definieren Sie ihn einfach als char lineBuffer[128]
, dann brauchen Sie ihn nicht freizugeben
btw3 verwenden Sie keine 'dynamisch dimensionierten Stack-Arrays' (definieren Sie das Array als char arrayName[some_nonconstant_variable]
), wenn Sie nicht genau wissen, was Sie tun, es funktioniert nur in C99.