c++ - ¿Se requiere que el tamaño de una estructura sea un múltiplo exacto de la alineación de esa estructura?
alignment sizeof (8)
Una vez más, estoy cuestionando una creencia de larga data.
Hasta hoy, creía que la alineación de la siguiente estructura normalmente sería 4 y el tamaño normalmente sería 5 ...
struct example
{
int m_Assume_32_Bits;
char m_Assume_8_Bit_Bytes;
};
Debido a este supuesto, tengo un código de estructura de datos que utiliza offsetof para determinar la distancia en bytes entre dos elementos adyacentes en una matriz. Hoy, vi un código antiguo que estaba usando sizeof donde no debía, no podía entender por qué no había tenido errores, programé una prueba de unidad, y la prueba me sorprendió al aprobar.
Un poco de investigación demostró que el tamaño del tipo que usé para la prueba (similar a la estructura anterior) era un múltiplo exacto de la alineación, es decir, 8 bytes. Tenía relleno después del último miembro. Aquí hay un ejemplo de por qué nunca esperé esto ...
struct example2
{
example m_Example;
char m_Why_Cant_This_Be_At_Offset_6_Bytes;
};
Un poco de Google mostró ejemplos que dejan en claro que este relleno después del miembro final está permitido, por ejemplo, http://en.wikipedia.org/wiki/Data_structure_alignment#Data_structure_padding (el bit "o al final de la estructura") .
Esto es un poco embarazoso, ya que recientemente publiqué este comentario: uso de struct padding (mi primer comentario a esa respuesta).
Lo que parece que no puedo determinar es si este relleno a un múltiplo exacto de la alineación está garantizado por el estándar de C ++, o si es solo algo que está permitido y que algunos compiladores (pero tal vez no todos) lo hacen.
Entonces, ¿es necesario que el tamaño de una estructura sea un múltiplo exacto de la alineación de esa estructura según el estándar de C ++?
Si el estándar C ofrece diferentes garantías, también me interesa eso, pero el enfoque está en C ++.
5.3.3 / 2
Cuando se aplica a una clase, el resultado [de sizeof] es el número de bytes en un objeto de esa clase, incluido cualquier relleno requerido para colocar objetos de ese tipo en una matriz.
Así que sí, el tamaño del objeto es un múltiplo de su alineación.
Así que para dividir su pregunta en dos:
1. ¿Es legal?
[5.3.3.2] Cuando se aplica a una clase, el resultado [del operador sizeof ()] es el número de bytes en un objeto de esa clase, incluido cualquier relleno requerido para colocar objetos de ese tipo en una matriz.
Entonces, no, no lo es.
2. Bueno, ¿por qué no lo es?
Aquí, sólo puedo especular.
2.1. La aritmética del puntero se pone más rara
Si la alineación fuera "entre elementos de la matriz" pero no afectara el tamaño, zthigns se complicaría innecesariamente, por ejemplo
(char *)(X+1) != ((char *)X) + sizeof(X)
(Tengo el presentimiento de que el estándar lo requiere implícitamente, incluso sin la declaración anterior, pero no puedo ponerlo a prueba)
2.2 Simplicidad
Si la alineación afecta el tamaño, la alineación y el tamaño se pueden decidir mirando un solo tipo. Considera esto:
struct A { int x; char y; }
struct B { A left, right; }
Con el estándar actual, solo necesito saber sizeof (A) para determinar el tamaño y el diseño de B.
Con el suplente que sugieres, necesito conocer las partes internas de A. De manera similar a tu example2
: para un "mejor empaque", sizeof (ejemplo) no es suficiente, debes considerar las partes internas de los ejemplos.
C ++ no lo dice explícitamente, pero es una consecuencia de otros dos requisitos:
Primero, todos los objetos deben estar bien alineados.
3.8 / 1 dice
La vida útil de un objeto de tipo
T
comienza cuando se obtiene el [...] almacenamiento con la alineación y el tamaño adecuados para el tipoT
y 3.9 / 5:
Los tipos de objetos tienen * requisitos de alineación (3.9.1, 3.9.2). La alineación de un tipo de objeto completo es un valor entero definido por la implementación que representa un número de bytes; un objeto se asigna en una dirección que cumple con los requisitos de alineación de su tipo de objeto.
Por lo tanto, cada objeto debe alinearse de acuerdo con sus requisitos de alineación.
El otro requisito es que los objetos en una matriz se asignen de forma contigua:
8.3.4 / 1:
Un objeto de tipo matriz contiene un conjunto de
N
subobjetos no vacíos de tipoT
asignados de manera contigua.
Para que los objetos en una matriz se asignen de forma contigua, no puede haber relleno entre ellos. Pero para que cada objeto de la matriz se alinee correctamente, cada objeto individual debe rellenarse para que el byte inmediatamente después del final del objeto también esté bien alineado. En otras palabras, el tamaño del objeto debe ser un múltiplo de su alineación.
El estándar dice (sección [dcl.array]
:
Un objeto de tipo matriz contiene un conjunto de N subobjetos no vacíos de tipo T. asignados de forma contigua.
Por lo tanto, no hay relleno entre los elementos de la matriz.
El relleno no requiere relleno dentro de las estructuras, pero el estándar no permite ninguna otra forma de alinear los elementos de la matriz.
La norma dice muy poco sobre el relleno y la alineación. Muy poco está garantizado. Sobre lo único en lo que puedes apostar es que el primer elemento está al principio de la estructura. Después de eso ... la alineación y el relleno pueden ser cualquier cosa.
No estoy seguro si esto está en el estándar C / C ++ real, y me inclino a decir que depende del compilador (solo para estar en el lado seguro). Sin embargo, tuve un tiempo "divertido" al descubrirlo hace unos meses, donde tuve que enviar estructuras C generadas dinámicamente como matrices de bytes a través de una red como parte de un protocolo, para comunicarme con un chip. La alineación y el tamaño de todas las estructuras tenían que ser coherentes con las estructuras en el código que se ejecuta en el chip, que se compiló con una variante de GCC para la arquitectura MIPS. Intentaré dar el algoritmo, y debería aplicarse a todas las variantes de gcc (y con suerte a la mayoría de los otros compiladores).
Todos los tipos de base, como char , short e int se alinean con su tamaño, y se alinean con la siguiente posición disponible, independientemente de la alineación del padre . Y para responder a la pregunta original, sí, el tamaño total es un múltiplo de la alineación.
// size 8
struct {
char A; //byte 0
char B; //byte 1
int C; //byte 4
};
Aunque la alineación de la estructura es de 4 bytes, los caracteres se empaquetan lo más cerca posible.
La alineación de una estructura es igual a la alineación más grande de sus miembros .
Ejemplo:
//size 4, but alignment is 2!
struct foo {
char A; //byte 0
char B; //byte 1
short C; //byte 3
}
//size 6
struct bar {
char A; //byte 0
struct foo B; //byte 2
}
Esto también se aplica a los sindicatos, y de una manera curiosa. El tamaño de una unión puede ser más grande que cualquiera de los tamaños de sus miembros, simplemente debido a la alineación:
//size 3, alignment 1
struct foo {
char A; //byte 0
char B; //byte 1
char C; //byte 2
};
//size 2, alignment 2
struct bar {
short A; //byte 0
};
//size 4! alignment 2
union foobar {
struct foo A;
struct bar B;
}
Usando estas reglas simples, debería poder determinar la alineación / tamaño de cualquier unión / estructura horriblemente anidadas que encuentre. Esto es todo de memoria, así que si me he perdido un caso de esquina que no se puede decidir a partir de estas reglas, ¡hágamelo saber!
Parece que el estándar C ++ 03 no dijo (o no encontré) si los bytes de relleno de alineación deberían incluirse en la representación del objeto.
Y el estándar C99 dice que el "tamaño de" un tipo de estructura o tipo de unión incluye relleno interno y posterior, pero no estoy seguro si todo el relleno de alineación está incluido en ese "relleno posterior".
Ahora volvamos a tu ejemplo. Realmente no hay confusión. sizeof(example) == 8
significa que la estructura toma 8 bytes para representarse a sí misma, incluidos los bytes de relleno de cola 3. Si el carácter en la segunda estructura tiene un desplazamiento de 6, sobrescribirá el espacio utilizado por m_Example
. El diseño de un determinado tipo está definido por la implementación y debe mantenerse estable en toda la implementación.
Sin embargo, si p+1
es igual a (T*)((char*)p + sizeof(T))
no está seguro. Y espero encontrar la respuesta.
Una definición de tamaño de alineación :
El tamaño de
alineaciónde una estructura es el desplazamiento de un elemento al siguiente elemento cuando tiene una matriz de esa estructura.
Por su naturaleza, si tiene una matriz de una estructura con dos elementos, ambos deben tener miembros alineados, por lo que eso significa que sí, el tamaño debe ser un múltiplo de la alineación. (No estoy seguro de si algún estándar lo aplica explícitamente, pero como el tamaño y la alineación de una estructura no dependen de si la estructura está sola o dentro de una matriz, las mismas reglas se aplican a ambas, por lo que realmente no puede sea de otra manera.)