txt manejo lenguaje leer guardarlo guardar ejercicios datos binarios arreglo archivos archivo c struct sizeof fwrite

manejo - Escribir contenido de estructura sin procesar(bytes) en un archivo en C. Confundido sobre el tamaño real escrito



leer un archivo y guardarlo en un arreglo c (8)

Pregunta básica, pero esperaba que esta estructura ocupara 13 bytes de espacio (1 para el carácter, 12 para los 3 elementos no firmados). En cambio, sizeof(ESPR_REL_HEADER) me da 16 bytes.

typedef struct { unsigned char version; unsigned int root_node_num; unsigned int node_size; unsigned int node_count; } ESPR_REL_HEADER;

Lo que trato de hacer es inicializar esta estructura con algunos valores y escribir los datos que contiene (los bytes sin procesar) al comienzo de un archivo, de modo que cuando abra este archivo, más tarde pueda reconstruir esta estructura y obtener meta datos sobre lo que contiene el resto del archivo.

Estoy inicializando la estructura y escribiéndola en el archivo de esta manera:

int esprime_write_btree_header(FILE * fp, unsigned int node_size) { ESPR_REL_HEADER header = { .version = 1, .root_node_num = 0, .node_size = node_size, .node_count = 1 }; return fwrite(&header, sizeof(ESPR_REL_HEADER), 1, fp); }

Donde node_size es actualmente 4 mientras experimento.

El archivo contiene los siguientes datos después de escribirle la estructura:

-bash$ hexdump test.dat 0000000 01 bf f9 8b 00 00 00 00 04 00 00 00 01 00 00 00 0000010

Espero que realmente contenga:

-bash$ hexdump test.dat 0000000 01 00 00 00 00 04 00 00 00 01 00 00 00 0000010

Disculpe la felicidad. Estoy tratando de aprender :) ¿Cómo puedo escribir eficientemente solo los componentes de datos de mi estructura en un archivo?


¡Intenta no hacer esto! La discrepancia de tamaño es causada por el relleno y la alineación utilizada por los compiladores / vinculadores para optimizar los accesos a los vars por velocidad. Las reglas de relleno y alineación con lenguaje y sistema operativo. Además, escribir y leer en diferentes hardware puede ser problemático debido a la endianidad.

Escribe tus metadatos byte a byte en una estructura que no se puede malinterpretar. Las cadenas ASCII terminadas en nulo son correctas.


Cuando se escriben estructuras tal como están con fwrite , se escribe entonces tal como están en la memoria, incluidos los "bytes muertos" dentro de la estructura que se insertan debido al relleno . Además, sus datos de múltiples bytes se escriben con los endiannes de su sistema.

Si no quiere que suceda, escriba una función que serialice los datos de su estructura. Puede escribir solo las áreas no acolchadas, y también escribir datos multibyte en un orden predecible (por ejemplo, en el orden de bytes de la red ).



Esto se debe a algo llamado alineación de memoria. La primera char se extiende para tomar 4 bytes de memoria. De hecho, los tipos más grandes como int solo pueden "comenzar" al comienzo de un bloque de 4 bytes, por lo que el compilador rellenará con bytes para llegar a este punto.

Tuve el mismo problema con el encabezado de mapa de bits, comenzando con 2 char. char bm[2] un char bm[2] dentro de la estructura y me pregunté por 2 días donde el # $% ^ el 3er y el 4to octeto del encabezado estaban yendo ...

Si desea evitar esto, puede usar __attribute__((packed)) pero tenga cuidado, la alineación de memoria ES necesaria para que su programa se ejecute convenientemente .


La estructura está sujeta a las reglas de alineación, lo que significa que algunos elementos se rellenan. Mirándolo, parece que el primer campo de caracteres unsigned char ha sido rellenado a 4 bytes.

Una de las trampas aquí es que las reglas pueden ser diferentes de un sistema a otro, así que si escribes la estructura como un todo usando fwrite en un programa compilado con un compilador en una plataforma, y ​​luego tratas de leerlo usando fread en otro, podría obtener basura porque el segundo programa asumirá que los datos están alineados para ajustarse a su concepción del diseño de la estructura.

En general, tienes que:

  1. Decida que los archivos de datos guardados solo son válidos para compilaciones de su programa que comparten ciertas características (dependiendo del comportamiento documentado del compilador que utilizó), o

  2. No se escribe una estructura completa como una, sino que se implementa un formato de datos más formal donde cada elemento se escribe individualmente con su tamaño controlado explícitamente.

(Un problema relacionado es que el orden de bytes podría ser diferente, la misma opción generalmente se aplica también allí, excepto que en la opción 2 desea especificar explícitamente el orden de bytes del formato de datos).


Los microprocesadores no están diseñados para captar datos de direcciones arbitrarias. Los objetos como int 4 bytes solo se deben almacenar en direcciones divisibles por cuatro. Este requisito se llama alineación .

C le da al compilador libertad para insertar bytes de relleno entre los miembros de la estructura para alinearlos. La cantidad de relleno es solo una variable entre diferentes plataformas, otra variable principal es la endianidad . Esta es la razón por la cual no debe simplemente "volcar" estructuras en el disco si desea que el programa se ejecute en más de una máquina.

La mejor práctica es escribir cada miembro explícitamente, y usar htonl para fijar la htonl a big-endian antes del resultado binario. Al leer de nuevo, use memcpy para mover bytes sin procesar, no use

char *buffer_ptr; ... ++ buffer_ptr; struct.member = * (int *) buffer_ptr; /* potential alignment error */

pero en lugar de hacer

memcpy( buffer_ptr, (char *) & struct.member, sizeof struct.member ); struct.member = ntohl( struct.member ); /* if member is 4 bytes */


Si desea escribir los datos en un formato específico, use matrices de caracteres unsigned char ...

unsigned char outputdata[13]; outputdata[0] = 1; outputdata[1] = 0; /* ... of course, use data from struct ... */ outputdata[12] = 0; fwrite(outputdata, sizeof outputdata, 1, fp);