Acolchado evitable con estructuras “no completas”

Trataba de dar un contraejemplo “lógico” a esta respuesta que indicaba que clasificar los miembros de una estructura en función de su tamaño minimizaría el relleno, cuando encontré lo que me parece ilógico.

Imagina la siguiente estructura:

struct A { int32_t a; int16_t b; }; 

sizeof esta estructura normalmente se rellena a 8 bytes para asegurarse de que a esté alineada, por ejemplo, en una matriz de struct A

Ahora imagina estas otras estructuras:

 struct B { struct A a, b; int16_t c, d; }; struct C { struct A a; int16_t c; struct A b; int16_t d; }; 

Como era de esperar, la struct B tiene el tamaño 20 debido al relleno. Sin embargo, hubiera esperado que la struct C tuviera un tamaño 16, ya que se podría evitar el relleno, pero ideone y gcc (con o sin optimización) dan un tamaño de 24 bytes, claramente rellenando 2 bytes después de cada uno de los miembros.

Mi razonamiento es que la struct A en realidad tiene solo 6 bytes y debe rellenarse cuando sea necesario, por ejemplo, en una matriz de struct A o su uso en struct B Sin embargo, en la struct C relleno de la struct A es innecesario y c podría haberse colocado donde podría haber sido el relleno de a y lo mismo con d y b .

¿Por qué el comstackdor no minimiza el relleno al poner c donde estaría el relleno de a ?


PS Entiendo que sizeof(struct A) debe devolver 8. De lo contrario, algo como memset(array_of_A, 0, N * sizeof *array_of_A) no funcionará correctamente ya que array_of_A contendría el relleno, mientras que N * sizeof *array_of_A ignoraría ese relleno.

Lo único en lo que puedo pensar que podría ser un problema es que, con la optimización anterior, sizeof(struct C) sería más pequeño que el tamaño de todos sus miembros. Sin embargo, no puedo pensar en un caso en que tal cosa pueda convertirse en un problema (es decir, un uso que no esté basado en un comportamiento indefinido).

memcpy(&someC.a, &someA, sizeof(someC.a)) escribiría sobre someC.c .

Eso es lo que estaba tratando de explicar con mi comentario acerca de que sizeof() tiene que ser diferente. Para que memcpy() , sizeof(someC.a) tendría que ser diferente de sizeof(someA) que parece estar pidiendo muchos problemas y es difícil encontrar errores.

 struct C someC; struct A someA; *(struct A*)&(someC.a) = someA; 

La asignación anterior puede fallar (escribir erróneamente en someC.c ) si los comstackdores admiten el relleno que usted describe.

EDITADO: El ejemplo anterior se basa en el comportamiento del comstackdor al asignar estructuras. Como he sabido (y recién comprobado), las copias de gcc ya que la estructura es una región plana de la memoria, que no corresponde a los miembros.

EDITADO: el cambio de “fallaría” a “puede fallar”, ya que no está definido si se copiarán los bits de relleno, consulte el artículo 6 de la sección 6.2.6.1 de ISO_IEC_9899_2011:

Cuando un valor se almacena en un objeto de estructura o tipo de unión, incluido en un objeto miembro, los bytes de la representación del objeto que corresponden a cualquier octeto de relleno toman valores no especificados.51)

y nota de pie de página 51) :

51) Por lo tanto, por ejemplo, la asignación de estructura no necesita copiar ningún bit de relleno.