Escribí esta función para leer una línea de un archivo:
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;
}
La función lee el archivo correctamente, y usando printf veo que la cadena constLine se leyó correctamente también.
Sin embargo, si utilizo la función, por ejemplo, así
while (!feof(myFile)) {
const char *line = readLine(myFile);
printf("%s\n", line);
}
printf produce un galimatías. ¿Por qué?
En su función readLine
, devuelve un puntero a la matriz line
(estrictamente hablando, un puntero a su primer carácter, pero la diferencia es irrelevante aquí). Como es una variable automática (es decir, está "en la pila"), la memoria se recupera cuando la función regresa. Se ve un galimatías porque printf
ha puesto sus propias cosas en la pila.
Necesitas devolver un buffer asignado dinámicamente desde la función. Ya tiene uno, es lineBuffer
; todo lo que tiene que hacer es truncarlo a la longitud deseada.
lineBuffer[count] = '\0';
realloc(lineBuffer, count + 1);
return lineBuffer;
}
Agregado (respuesta a la pregunta de seguimiento en el comentario): readLine
devuelve un puntero a los caracteres que componen la línea. Este puntero es lo que necesitas para trabajar con el contenido de la línea. También es lo que debe pasar a free
cuando haya terminado de usar la memoria ocupada por estos caracteres. Así es como puede utilizar la función readLine
:
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()
devuelve un puntero a una variable local, lo que provoca un comportamiento indefinido.
Para evitarlo se puede:
readLine()
.línea
usando malloc()
- en este caso línea
será persistenteconst 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);
}
nota que la variable 'line' se declara en la función de llamada y luego se pasa, por lo que su función readLine
llena el buffer predefinido y simplemente lo devuelve. Esta es la forma en que la mayoría de las bibliotecas de C trabajan.
Hay otras formas, de las que soy consciente:
char line[]
como estático
(static char line[MAX_LINE_LENGTH]
-> mantendrá su valor DESPUÉS de regresar de la función). -mal,
la función no es reentrante, y
condición de carrera puede ocurrir -> si usted
dos veces desde dos hilos, se
sobreescribirá sus resultadosmalloc()
ing la línea char[], y
liberándolo en las funciones de llamada ->
demasiados malloc
s caros, y,
delegar la responsabilidad de liberar el buffer a otra función (la solución más elegante es llamar a malloc
y free
en cualquier buffer en la misma función)por cierto, 'explícitamente' el casting de char*
a const char*
es redundante.
btw2, no hay necesidad de malloc()
el lineBuffer, simplemente define char lineBuffer[128]
, así no necesitas liberarlo
btw3 no uses #39;arrays de pila de tamaño dinámico' (definiendo el array como
char arrayName[some_nonconstant_variable]`), si no sabes exactamente lo que estás haciendo, sólo funciona en C99.