c++ - tecnicas - tipos de material para relleno
¿Por qué funciona este truco de relleno estructural? (2)
Considera este simple programa
#include <iostream>
struct A
{
int x1234;
short x56;
char x7;
};
struct B : A
{
char x8;
};
int main()
{
std::cout << sizeof(A) << '' '' << sizeof(B) << ''/n'';
return 0;
}
Esto imprime 8 12
. A pesar de que B
se puede empaquetar en 8 bytes sin romper los requisitos de alineación, ocupa 12 codiciosos bytes.
Sería bueno tener sizeof(B) == 8
, pero la respuesta a ¿Es el tamaño de una estructura requerida para ser un múltiplo exacto de la alineación de esa estructura? sugiere que no hay una manera.
Por lo tanto, me sorprendió cuando la siguiente
struct MakePackable
{
};
struct A : MakePackable
{
int x1234;
short x56;
char x7;
};
struct B : A
{
char x8;
};
impreso 8 8
.
¿Que esta pasando aqui? Sospecho que los tipos de diseño estándar tienen algo que ver con eso. Si es así, ¿cuál es la razón por la que causa el comportamiento anterior, cuando el único propósito de esa característica es garantizar la compatibilidad binaria con C?
EDITAR: Como otros han señalado que esto es ABI o compilador específico, entonces debo agregar que este comportamiento se observó en x86_64-unknown-linux-gnu con los siguientes compiladores:
- clang 3.6
- gcc 5.1
También noté algo extraño en el dumper struct de clang. Si solicitamos el tamaño de datos sin relleno de cola ("dsize"),
A B
first 8 9
second 7 8
luego en el primer ejemplo obtenemos dsize(A) == 8
. ¿Por qué esto no es 7?
Este es un punto de datos, aunque no es una respuesta completa.
Digamos que tenemos (como una unidad de traducción completa, no como un fragmento):
struct X {};
struct A
{
int x1234;
short x56;
char x7;
}
void func(A &dst, A const &src)
{
dst = src;
}
Con g ++, esta función se compila para:
movq (%rdx), %rax
movq %rax, (%rcx)
Sin embargo, si se usa struct A : X
, esta función es:
movl (%rdx), %eax
movl %eax, (%rcx)
movzwl 4(%rdx), %eax
movw %ax, 4(%rcx)
movzbl 6(%rdx), %eax
movb %al, 6(%rcx)
Estos dos casos corresponden en realidad a los tamaños que son 8 12
y 8 8
respectivamente en el ejemplo de OP.
La razón para esto es bastante clara: A
podría usarse como base para alguna clase B
, y luego la llamada func(b, a);
debe tener cuidado de no molestar a otros miembros de b
que puedan residir en el área de relleno ( b.x8
en el ejemplo de OP);
No puedo ver ninguna propiedad particular de A : X
en el estándar de C ++ que haría que g ++ decidiera que el relleno es reutilizable en la struct A : X
, pero no en la struct A
Tanto A
como A : X
son triviales , se pueden copiar , diseño estándar y POD .
Supongo que debe ser solo una decisión de optimización basada en el uso típico. La versión sin reutilización será más rápida de copiar. Tal vez un diseñador de G ++ ABI podría comentar?
Curiosamente, este ejemplo muestra que ser trivialmente copiable no implica que memcpy(&b, &a, sizeof b)
sea equivalente a b = a
!
No soy un verdadero abogado de C ++, sin embargo, lo que he encontrado hasta ahora es:
Al hacer referencia a las respuestas en esta pregunta , una estructura solo sigue siendo un POD de diseño estándar, mientras que solo hay 1 clase con miembros no estáticos entre sí y sus clases principales. Entonces, bajo esa idea, A
tiene un diseño garantizado en ambos casos, pero B
no lo hace en ningún caso.
Esto se apoya en el hecho de que std::is_pod es verdadero para A
y falso para B
en ambos.
- Primer caso: http://ideone.com/jyPb5J
- Segundo caso: http://ideone.com/bYcLXa
Entonces, si estoy entendiendo esto correctamente, el compilador tiene espacio para hacer lo que quiera con el diseño de B
en ambos casos. Y, aparentemente, en el segundo caso se siente como utilizar lo que de otro modo hubiera sido el byte de relleno de A