Comprensión de las variaciones del tamaño de las estructuras en C/C++: el impacto del ordenamiento de los miembros y la alineación de los datos
¿Por qué el tamaño de struct bit_man1 genera 8 bytes mientras que struct bit_man2 genera 4 bytes, aunque ambas estructuras contienen los mismos tipos y cantidad de datos? Considere las estructuras definidas a continuación:
#include <stdio.h>
// packing is done in 4 bytes size since int is the biggest in struct
struct bit_man1 {
int a:5; //5 bits
int b:8; //5+8 =13 bits
char abc[2]; //2 bytes 8 +8 +13= 29 bit
int z:2; // 2 bits 29+2 =31
};
struct bit_man2 {
int a:5; //5 bits
int b:8; //5+8 =13 bits
int z:2; // 2 bits 13+2=15
char abc[2]; //2 bytes 8 +8 +15= 31 bit
};
int main() {
printf("Size of bit_man1: %zu bytes\n", sizeof(struct bit_man1));
printf("Size of bit_man2: %zu bytes\n", sizeof(struct bit_man2));
return 0;
}
versión gcc
$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/11/lto-wrapper
OFFLOAD_TARGET_NAMES=nvptx-none:amdgcn-amdhsa
OFFLOAD_TARGET_DEFAULT=1
Target: x86_64-linux-gnu
Configured with: ../src/configure -v --with-pkgversion='Ubuntu 11.4.0-1ubuntu1~22.04' --with-bugurl=file:///usr/share/doc/gcc-11/README.Bugs --enable-languages=c,ada,c++,go,brig,d,fortran,objc,obj-c++,m2 --prefix=/usr --with-gcc-major-version-only --program-suffix=-11 --program-prefix=x86_64-linux-gnu- --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --libdir=/usr/lib --enable-nls --enable-bootstrap --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --with-default-libstdcxx-abi=new --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --enable-default-pie --with-system-zlib --enable-libphobos-checking=release --with-target-system-zlib=auto --enable-objc-gc=auto --enable-multiarch --disable-werror --enable-cet --with-arch-32=i686 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-offload-targets=nvptx-none=/build/gcc-11-XeT9lY/gcc-11-11.4.0/debian/tmp-nvptx/usr,amdgcn-amdhsa=/build/gcc-11-XeT9lY/gcc-11-11.4.0/debian/tmp-gcn/usr --without-cuda-driver --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu --with-build-config=bootstrap-lto-lean --enable-link-serialization=2
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 11.4.0 (Ubuntu 11.4.0-1ubuntu1~22.04)
Pero obtenemos una salida como esa.
Size of bit_man1: 8 bytes
Size of bit_man2: 4 bytes
Como el empaquetado prioriza 4 bytes, ¿no debería empaquetarse el int z:2 (2 bits) en los 29 bits, lo que da como resultado 31 bits? ¿Hay alguna razón por la cual el compilador asigna otros 4 bytes?
Los campos de bits adyacentes se pueden agrupar en la misma unidad de asignación, pero los campos de bits y los que no lo son, no. Esto significa que el abc
miembro ocupará 2 bytes completos y no ocupará ningún byte parcial, como un bit no utilizado en una unidad que contiene un campo de bits.
En el primer caso, se comienza con dos campos de bits que suman un total de 13 bits. Estos encajan en una única unidad de asignación de 2 bytes. La char
matriz ocupa los siguientes 2 bytes completos. Luego, el campo de bits restante ocupa otro byte. Luego, dado que el tipo base de los campos de bits es int
la estructura, se completan otros 3 bytes (suponiendo que int
ocupa 4 bytes) para un total de 8.
En el segundo caso se empieza con tres campos de bits que suman un total de 15 bits. Estos encajan en una única unidad de asignación de 2 bytes como en el caso anterior. La char
matriz ocupa los siguientes 2 bytes completos para un total de 4 bytes utilizados.