2011-02-02 13 views
8

Quiero capturar parte de los datos dentro de algunas estructuras C para serializarlos/deserializarlos parcialmente, escribiendo los bytes de la memoria en el disco, y viceversa.sizeof() parte de una estructura C - tipo de

Las estructuras no se conocen de antemano, se construyen dinámicamente con mi propio generador de código C (así como el código que lo serializará). Los campos serializables se colocarán al comienzo de la estructura.

Digamos que una tiene una estructura con 4 campos, y los dos primeros son para ser serializado:

typedef struct { 
    int8_t x1; 
    int32_t x2; /* 1 + 4 = 5 bytes (if packed) */ 
    int8_t y1; 
    int32_t y2; /* 1 + 4 +1 + 4 = 10 bytes (if packed) */ 
} st; 

planeo para agarrar el puntero a la variable de estructura y escribir/leer los n bytes que cubren los dos primeros campos (x1, x2). No creo que deba preocuparme por la alineación/empaque porque no pretendo que la serialización sobreviva a diferentes compilaciones (solo se espera que un ejecutable único lea/escriba los datos). Y, como me estoy enfocando en una amplia gama de compiladores-arquitecturas, no quiero poner suposiciones sobre el alineamiento-embalaje o trucos específicos del compilador.

Entonces, necesito contar los bytes. Y no puedo simplemente hacer sizeof(st.x1)+sizeof(st.x2) debido a alingment-padding. Por lo tanto, tengo la intención de restar puntos, desde el principio de la estructura al primer campo "no persistente":

st myst; 
int partsize = (char*)&myst.y1 - (char*)(&myst); 
printf("partial size=%d (total size=%d)\n",partsize,sizeof(myst)); 

Esto parece funcionar. Y puede colocarse en una macro.

(Para el registro: intenté también escribir otra macro que no requiera una instancia de la estructura, algo así como this, pero no parece posible aquí, pero esto no me importa demasiado).

Mi pregunta: ¿Es esto correcto y seguro? ¿Puedes ver alguna trampa potencial, o algún mejor enfoque?

Entre otras cosas: ¿El estándar C (y los compiladores de facto) asumen que los campos de estructuras se encuentran en la memoria en el mismo orden en que están definidos en la fuente? Esto probablemente es una pregunta estúpida, pero yo quiero estar seguro ...

ACTUALIZACIÓN: Algunas conclusiones de las respuestas y mis propias conclusiones:

  1. No parece haber ningún problema con mi enfoque . En particular, C dicta que los campos de estructura nunca cambiarán el orden.

  2. Uno también podría (según lo sugerido por un aswer) contar desde el último campo persistente y agregar su tamaño: (char*)&myst.x2 + sizeof(&myst.x2) - (char*)(&myst). Eso sería equivalente, excepto que incluiría no los bytes de relleno (si están presentes) para el último campo. Una ventaja muy pequeña, y una desventaja muy pequeña, en ser menos simple.

  3. Pero la respuesta aceptada, con offsetof, parece ser preferible a mi propuesta. Es claro, expresivo y puro tiempo de compilación, no requiere una instancia de la estructura. Además, parece ser estándar, disponible en cualquier compilador. Si uno no necesita una construcción en tiempo de compilación, y tiene una instancia de la estructura disponible (como es mi caso), ambas soluciones son esencialmente equivalentes.

+0

Pregunta relacionada: http://stackoverflow.com/questions/2748995/c-struct-memory-layout – payne

+0

Quité la etiqueta C++, ya que no hay C++ en algún lugar cerca de esta pregunta. – Puppy

Respuesta

10

¿Has mirado las instalaciones offsetof? Devuelve el desplazamiento de un miembro desde el inicio de una estructura. Por lo tanto, offsetof (st, x2) devuelve el desplazamiento de x2 desde el inicio de la estructura. Por lo tanto, en su ejemplo, offsetof (st, x2) + sizeof(st.x2) le dará el recuento de bytes de los componentes serializados.

Esto es bastante similar a lo que está haciendo ahora, que acaba de llegar a ignorar el relleno después de x2 y utilizar una pieza rara vez se utiliza de C.

+0

+1: Muéstremela :) – Lars

+0

Gran sugerencia, no lo sabía (¿o lo recuerda?). Lamentablemente, parece no ser omnipresente. MINGW gcc, en particular, le falta. Pero mirar su definición en otros compiladores es de gran valor. – leonbloy

+0

@leonbloy: 'offsetof()' es C estándar, pero debe incluir '' para obtenerlo. – caf

0

Es posible que desee echar un vistazo a protobuf; parece hacer lo que quieras de manera portátil.

5

C garantiza este tipo de comportamiento. Está destinado a permitir el polimorfismo primitivo. Considere:

struct X { 
    int a; 
}; 
struct Y { 
    int a; 
    int b; 
}; 
void foo(X* x) { 
    x->a = 10; 
}; 
Y y; 
foo((X*)&y); // Well defined behaviour- y.a = 10. 
3

El compilador C puede insertar bytes de relleno para la alineación, pero no puede reordenar las variables de estructura.

Una manera más limpia podría ser simplemente definir una segunda estructura para el tamaño de(), que incluye las variables de inicio de la estructura que desea. El compilador garantizará que 2 puntales que tienen las mismas variables en el mismo orden se distribuirán de la misma manera.

+0

+1 Buena sugerencia. Pero en mi escenario -lotes de estructuras con muchos campos- este enfoque resultaría demasiado invasivo (si redefinir las estructuras, anidamiento), o introducir demasiado desorden (si creo una estructura gemela para cada original). – leonbloy

0

Yo voto por el principio KISS. Escriba en el elemento del archivo por elemento y le garantizamos que no habrá dependencias del compilador.