limpiar - ¿Por qué mi simple programa C muestra la basura a stdout?
stdin stdout stderr (5)
Puede usar calloc
lugar de malloc
para asignar memoria que ya está inicializada. calloc
adquiere un argumento extra. Es útil para asignar matrices; El primer parámetro de calloc
indica la cantidad de elementos en la matriz a la que le gustaría asignar memoria, y el segundo argumento es el tamaño de cada elemento. Como el tamaño de un char
es siempre 1, podemos pasar 1
como segundo argumento:
buffer = calloc (fileLen + 1, 1);
En C, no hay necesidad de lanzar el valor de retorno de malloc
o calloc
. Lo anterior asegurará que la cadena terminará nula incluso si la lectura del archivo finalizó prematuramente por cualquier razón. calloc
tarda más tiempo que malloc
porque tiene que poner a cero toda la memoria que pediste antes de dártela.
Considere el siguiente programa simple de C que lee un archivo en un búfer y muestra ese búfer a la consola:
#include<stdio.h>
main()
{
FILE *file;
char *buffer;
unsigned long fileLen;
//Open file
file = fopen("HelloWorld.txt", "rb");
if (!file)
{
fprintf(stderr, "Unable to open file %s", "HelloWorld.txt");
return;
}
//Get file length
fseek(file, 0, SEEK_END);
fileLen=ftell(file);
fseek(file, 0, SEEK_SET);
//Allocate memory
buffer=(char *)malloc(fileLen+1);
if (!buffer)
{
fprintf(stderr, "Memory error!");
fclose(file);
return;
}
//Read file contents into buffer
fread(buffer, fileLen, 1, file);
//Send buffer contents to stdout
printf("%s/n",buffer);
fclose(file);
}
El archivo que leerá simplemente contiene:
Hola Mundo!
El resultado es:
¡Hola mundo! ²²²²▌▌▌▌▌▌▌↔☺
Ha pasado un tiempo desde que hice algo significativo en C / C ++, pero normalmente asumiría que el búfer se estaba asignando más grande de lo necesario, pero este no parece ser el caso.
fileLen termina siendo 12, que es preciso.
Ahora estoy pensando que debo mostrar el búfer equivocado, pero no estoy seguro de lo que estoy haciendo mal.
¿Puede alguien darme una pista de lo que estoy haciendo mal?
El enfoque de JesperE funcionará, pero puede interesarte saber que hay una forma alternativa de manejar esto.
Siempre puede imprimir una cadena de longitud conocida, incluso cuando no hay terminador NUL, al proporcionar la longitud para printf
como la precisión para el campo de cadena:
printf("%.*s/n", fileLen, buffer);
Esto le permite imprimir la cadena sin modificar el búfer.
JesperE tiene razón con respecto al problema de nul-terminación en su ejemplo, solo agregaré que si está procesando archivos de texto, sería mejor usar fgets () o algo similar, ya que manejará adecuadamente las secuencias de nueva línea en diferentes plataformas y siempre nul-termina la cadena por ti. Si realmente está trabajando con datos binarios, entonces no desea usar printf () para generar los datos ya que las funciones de impresión esperan cadenas y un byte nulo en los datos causará el truncamiento de la salida.
Necesitas terminar NUL tu cadena. Añadir
buffer[fileLen] = 0;
antes de imprimirlo
Su enfoque para determinar el tamaño del archivo buscando hasta el final del archivo y luego usando ftell()
es incorrecto:
- Si se trata de un archivo de texto, abierto sin
"b"
en el segundo parámetro de la llamadafopen()
, entoncesftell()
puede no decirle la cantidad de caracteres que puede leer del archivo. Por ejemplo, Windows usa dos bytes para el final de la línea, pero cuando se lee, es unchar
. De hecho, el valor de retorno deftell()
para las transmisiones abiertas en modo texto es útil solo en las llamadas afseek()
, y no para determinar el tamaño del archivo. - Si se trata de un archivo binario, abierto con
"b"
en el segundo parámetro parafopen()
, entonces el estándar C tiene esto que decir:Establecer el indicador de posición del archivo al final del archivo, como con
fseek(file, 0, SEEK_END)
, tiene un comportamiento indefinido para una secuencia binaria (debido a posibles caracteres nulos finales) o para cualquier transmisión con codificación dependiente del estado que no funciona Con seguridad termina en el estado de cambio inicial.
Por lo tanto, lo que está haciendo no necesariamente va a funcionar en la norma C. Su mejor fread()
es usar fread()
para leer, y si necesita más memoria, use realloc()
. Su sistema puede proporcionar mmap()
, o puede hacer garantías acerca de cómo configurar el indicador de posición del archivo al final del archivo para transmisiones binarias, pero depender de ellas no es portátil.
Ver también este C-FAQ: ¿Cuál es la diferencia entre el texto y la E / S binaria? .