Evitar el relleno de la estructura en C

¿Cómo podemos evitar el relleno de la estructura en C además del uso de pragma pack aur bit field? ¿Hay algún otro método disponible?

Empaque los artículos más grandes al inicio de la estructura. Empaque los artículos más pequeños hacia el final.

struct optimal_packing { double d; int i[4]; short j[3]; char s[24]; }; 

Para ser un poco más precisos, son los elementos con los requisitos de alineación más estrictos los que deben aparecer antes (los que tienden a ser punteros y el double o quizás el long double ), y aquellos con los requisitos de alineación menos estrictos al final ( short y de caracteres) . Aún puede terminar con el relleno de la cola si la longitud total de los componentes sum, por ejemplo, 35 bytes, pero uno de los tipos requiere una alineación de 8 bytes; Habría 5 bytes de relleno.

La única forma completamente portátil y confiable de evitar el relleno de la estructura es no usar miembros reales en su struct en absoluto. Utilice un solo miembro de la matriz de caracteres y defina macros que accedan a su contenido:

 struct paddingless { // char *p; // double d; // char c; #define OFFSET_P 0 #define OFFSET_D (OFFSET_P + sizeof(char *)) #define OFFSET_C (OFFSET_D + sizeof(double)) #define OFFSET_END (OFFSET_C + sizeof(char)) char data[OFFSET_END]; }; 

Los recolectores y setters portátiles se verían así:

 inline double paddingless_get_d(const struct paddingless *o) { double val; memcpy(&val, o->data + OFFSET_D, sizeof(val)); return val; } inline void paddingless_set_d(struct paddingless *o, double val) { memcpy(o->data + OFFSET_D, &val, sizeof(val)); } 

Si sabe que su architecture acepta acceso no alineado, puede salir con un definidor definido con un reparto:

 #define paddingless_get_d(o) (*(double *) ((o)->data + OFFSET_D)) #define paddingless_set_d(o, val) (*(double *) ((o)->data + OFFSET_D) = (val)) 

Esto no es portátil, pero potencialmente más rápido que la alternativa. Y funciona en una vax x86 …

Hay algunas cosas a considerar. En primer lugar, el comstackdor agrega el relleno por una razón: trata de hacer el código de máquina óptimo y de trabajo para su plataforma designada. Así que normalmente el relleno es una buena cosa. Excepto en los casos en los que desea definir protocolos de comunicaciones de datos, asignaciones de registro de hardware y similares, donde el número de bytes simplemente debe coincidir con una especificación determinada.

Realmente no hay una forma estándar de evitar el relleno. Lo mejor que puede lograr es un aserto de tiempo de comstackción que da un error si el tamaño de la estructura no coincide con la sum de todos sus miembros individuales. Cuando la afirmación falla, ve y cambia la configuración del comstackdor para bloquear el relleno. Preferiblemente:

static_assert(sizeof(mystruct) == (sizeof(mystruct.x) + sizeof(mystruct.y) +...));

Si static_assert no está disponible en su comstackdor de C (necesitaría uno compatible con C11), entonces use alguna macro “ct_assert”, puede encontrar mucho en este sitio.

Sin embargo, esto no resuelve el problema de manera portátil, dependería de la configuración del comstackdor. La única forma verdaderamente portátil de resolver el problema es algo como esto:

 void mystruct_copy_from (uint8_t* raw_data, const mystruct_t* ms) { memcpy(raw_data, &ms->x, sizeof(ms->x)); raw_data += sizeof(ms->x); memcpy(raw_data, &ms->y, sizeof(ms->y)); raw_data += sizeof(ms->y); // ... and so on } uint8_t protocol [EXPECTED_STRUCT_SIZE]; mystruct_copy_from (protocol, &mystruct); send(protocol); // some data communication interface without padding