8 de Febrero, 2005

Sizeof madness

¡Pasen! ¡Pasen y vean! Se trata de un problema que es recursivo en mi vida, viene de vez en cuando para darme dolores de cabeza hasta que recuerdo que esto me pasó hace ya unos años, otra vez :P.

Para que no se me vuelva a olvidar:

#include<stdio.h>

struct structMadness
{
        int a;
        char b;
};

int
main()
{
        struct structMadness sm;

        printf("sizeof(int)=%d\n", sizeof(int));
        printf("sizeof(char)=%d\n", sizeof(char));
        printf("sizeof(sm)=%d\n", sizeof(sm));

        return 0;
}

Un programa sencillo, ¿no? Tenemos una estructura con un entero y un caracter, y mostramos por pantalla el tamaño de un entero, un caracter y de la estructura.

$ gcc main.c -o main
$ ./main
sizeof(int)=4
sizeof(char)=1
sizeof(sm)=8

¡Ahí está! El tamaño de a (4 bytes) más el tamaño de b (1 byte), suma... ¡8 bytes!

Pruébenlo en casa, no muerde.

Resulta que el compilador, al reservar memoria para la estructura, está alineándola a 32 bits (en mi caso el int son 32 bits, el char son 8 bits, así que añade 24 bits más para ser múltiplo de 32). Esto podemos comprobarlo con un gcc -S main.c para ver el código ensamblador que genera, donde se aprecia un align 4 (en mi caso, GCC 2.95.4).

Bueno, esto es así y hay que convivir con ello. Cualquier compilador nos permitirá compactar la estructura de forma que no haya alineamiento (bueno, sí lo hay... pero es al byte, lo cual no nos causará problemas).

Cambiamos la estructura por:

struct structMadness
{
        int a;
        char b;
} __attribute__ ((packed));

Ahora repetimos la ejecución:

$ ./main
sizeof(int)=4
sizeof(char)=1
sizeof(sm)=5

Bien, esto ya es más razonable ;).

¿Dónde puede ser esto un problema? Cuando leemos una estructura de disco. Si se grabó compactada, hay que leerla compactada, sino el alineamiento nos hará leer de más (o de menos si leemos compactando una estructura que se grabó sin compactar :D).

Además podría darse un caso en el que el alineamiento no fuera siempre de 32 bits, con los que datos guardados/leídos con alineamientos diferentes llevarían a error al recuperar los datos guardados.

Espero que no se me vuelva a olvidar.

Anotación por Juan J. Martínez.

Hay 2 comentarios

Gravatar

No entiendo mucho de estos temas pero creo que los compiladores reservan memoria para las estructuras con tamaño alineado a un número múltiplo del tamaño mayor del tipo de dato. Por eso ese struct ocupa 8bytes en memoria no? (por el tipo de datos int, 4 bytes, más 1 byte de char hacen 8 bytes (múltiplo de 4))

Lo de compactar estructuras es nuevo para mí :p voy a mirar porque parece estar bien para programillas que trabajen siempre en memoria.

Saludos

por ebarbeito, en 2005-02-08 17:05:32

Gravatar

Si tienes que grabar/leer una estructura compleja a/de disco, una struct es la forma natural de hacerlo, pero sin compactar (el término no se si será ese en castellano...), no funcionará siempre como se espera porque la disposición en memoria no corresponde con la distribución de los elementos y sus tamaños.
Grabar/leer 1 elemento (estructura) es más sencillo que hacerlo con cada uno de sus componentes, así que es bueno conocer estas particularidades...

por Juanjo, en 2005-02-08 22:30:38

Los comentarios están cerrados: los comentarios se cierran automáticamente una vez pasados 30 días. Si quieres comentar algo acerca de la anotación, puedes hacerlo por e-mail.