2010-11-21 23 views
11

Hoy en día, con mi gran sorpresa, descubrí queC/C++ conseguir el tamaño struct

Cuando se aplica el operador sizeof a una clase, estructura o tipo de unión, el resultado es el número de bytes en una objeto de ese tipo, más cualquier relleno añadido para alinear miembros en límites de palabras. El resultado no se corresponde necesariamente con el tamaño calculado al agregar los requisitos de almacenamiento de los miembros individuales.

que no sabía de él, y estoy bastante seguro de que esta cosa se está rompiendo algunos de mi viejo código: leer archivos binarios que solía tener estructuras como ésta:

struct Header 
{ 
    union { 
     char identc[4]; 
     uint32 ident; 
    }; 
    uint16 version; 
}; 

ya leer los 6 bytes directamente con fread impulsado por sizeof:

fread(&header, sizeof(header), 1, f); 

Pero ahora vuelve sizeof(header)8!


¿Es posible que con versiones anteriores del CCG sizeof(header) regresaron 6, o que mi mente está totalmente desaparecido?

De todos modos, ¿hay algún otro operador (o directiva de preprocesador o lo que sea) que le permita al compilador saber qué tan grande es la estructura, sin incluir el relleno?

De lo contrario, ¿cuál sería una forma limpia de leer una estructura de datos sin procesar desde un archivo que no requiere escribir demasiado código?


EDITAR: Sé que esta no es la forma correcta de leer/escribir datos binarios: Tendría resultado diferente dependiendo de endianess máquina y esas cosas. De todos modos, este método es el más rápido, estoy tratando de leer algunos datos binarios para obtener rápidamente su contenido, no para escribir una buena aplicación que voy a utilizar en el futuro o para lanzar.

+1

posible duplicado de [¿Por qué no es sizeof para una estructura igual a la suma de sizeof de cada miembro?] (Http://stackoverflow.com/questions/119123/why-isnt-sizeof-for-a-struct -equal-a-la-suma-de-tamaño-de-cada-miembro) – cdhowie

+0

solo una observación: el estándar-C tampoco permite miembros 'union' sin nombre; también, los tipos estándar de tamaño fijo (declarados en 'stdint.h') se llaman' uint32_t' y 'uint16_t' – Christoph

+0

Sería útil citar de dónde vino su cita. ¿Es esto de un estándar reciente? – beldaz

Respuesta

9

Lo que desea es el comando #pragma pack. Esto le permite configurar el embalaje a cualquier cantidad que desee. Normalmente, establecería el valor de empaque en 1 (¿o es 0?) Antes de la definición de su estructura y luego lo devolverá al valor predeterminado después de la definición.

Tenga en cuenta que esto no hace nada para garantizar la portabilidad entre sistemas.

Consulte también: use-of-pragma-in-c y varias otras preguntas sobre SO

+0

Esto es exactamente lo que necesitaba. Gracias. – peoro

+0

Esta respuesta debe mencionar que es específica de Wintel. –

+0

@R ..: en realidad está trabajando con GCC en Linux (sí, lo probé en arquitecturas x86, pero no veo por qué no debería funcionar con GCC en ninguna otra plataforma). – peoro

3

Sí, el código que ha presentado no es portátil. No solo los tamaños de estructura sino también los pedidos de bytes pueden diferir.

1

La mayoría de las compilaciones proporcionan una extensión específica que le permite controlar el empaque de las estructuras. Esto debería permitirle controlarlo. Sin embargo, cuando escriba la estructura en binario, debería poder simplemente escribirla y leerla independientemente del empaque, ya que cuando escribe la estructura, también debe escribir sizeof (struct) bytes. El único caso en el que esto sería un problema es si deseaba leer los archivos creados con las versiones anteriores. Además, debe tener en cuenta los problemas de orden de bytes, etc.

+0

No generé los datos binarios de la misma manera. Es un archivo que necesitaba analizar rápidamente. – peoro

1

Su pregunta es compilador específico, pero en general, si se construye la estructura de tal manera que cada miembro se encuentra en un límite del mismo tamaño que en sí (cuatro elementos de bytes en límites divisibles por cuatro, etc.), obtendrás el comportamiento que deseas. Observe también casos como el que presentó en el que el relleno viene al final de una estructura para alinear el inicio del primer elemento de la siguiente estructura, si se dispusieron en una matriz.

1

Parece que no has hecho una pregunta, ¡así que no estoy seguro de por qué estoy tratando de responder! Pero sí, el embalaje es importante y cambiará según las versiones del compilador, los indicadores, los pragmas de la arquitectura de destino, la dirección del viento, las fases de la luna y potencialmente muchas otras cosas. Volcar el binario a un archivo (o socket) no es una muy buena manera de serializar algo.

+0

Bueno, la pregunta principal, aparte de las dudas sobre el relleno, era: "De lo contrario, ¿cuál sería una forma de leer una estructura de datos sin formato desde un archivo que no requiere escribir demasiado código?" – peoro

0

Sí, el problema de alineación. Es por eso que los mensajes del protocolo de Internet tienen estructuras alineadas para evitar este problema al enviar datos a través de la red.

Lo que puede hacer es corregir sus estructuras para que estén alineadas correctamente, o tener funciones de ordenación que usa al guardar y recuperar datos.

1

Este relleno adicional es necesario para alinear correctamente los miembros cuando se crea una matriz de estas estructuras. Sin él, el segundo elemento de la matriz tendría la ident miembro alineados en una dirección que no es un múltiplo de 4.

Es probable que sea demasiado tarde para hacer nada al respecto, es probable que escribió archivos con esta estructura antes de . Cambiar el embalaje hará que estos archivos sean ilegibles. Pero, sí, tener datos de archivo que dependen de la configuración del compilador no es la mejor idea. Tener datos almacenados en un formato legible para las personas es común en estos días. Ni los bytes de disco ni los ciclos de CPU lo valen.

+0

No generé datos binarios de la misma manera. Es un archivo que necesitaba analizar rápidamente. – peoro

+0

Bueno, técnicamente necesitarías averiguar qué embalaje se usó en cualquier programa que haya escrito los datos. Sabrá cuando necesite averiguar, obtendrá valores de basura. Por lo general, a partir de los datos que se escribieron después de esa estructura. –

2

Esta no es la forma correcta de procesar archivos binarios. Además de los problemas de alineación, también tiene problemas endian. La forma correcta de leer archivos binarios es con una matriz de uint8_t (o unsigned char, realmente no importa) y sus propias funciones para construir una representación en memoria de los datos.