Pregunta

Para aclarar mi pregunta, vamos a empezar con un programa de ejemplo:

#include <stdio.h>

#pragma pack(push,1)
struct cc {
    unsigned int a   :  3;  
    unsigned int b   : 16;
    unsigned int c   :  1;
    unsigned int d   :  1;
    unsigned int e   :  1;
    unsigned int f   :  1;
    unsigned int g   :  1;
    unsigned int h   :  1;
    unsigned int i   :  6;  
    unsigned int j   :  6;  
    unsigned int k   :  4;  
    unsigned int l   : 15;
};
#pragma pack(pop)

struct cc c;

int main(int argc, char **argv)

{   printf("%d\n",sizeof(c));
}

La salida es "8", lo que significa que los 56 bits (7 bytes) I quiere embalar se empaquetan en 8 bytes, aparentemente perder un byte conjunto. Curiosidad acerca de cómo el compilador estaba colocando estos bits en la memoria, he intentado escribir valores específicos a &c, por ejemplo:.

int main (int argc, char ** argv)

{
unsigned long long int* pint = &c;
*pint = 0xFFFFFFFF;
printf("c.a = %d", c.a);
...
printf("c.l = %d", c.l);
}

Como era de esperar, en x86_64 utilizando Visual Studio 2010, ocurre lo siguiente:

*pint = 0x00000000 000000FF :

c[0].a = 7
c[0].b = 1
c[0].c = 1
c[0].d = 1
c[0].e = 1
c[0].f = 1
c[0].g = 0
c[0].h = 0
c[0].i = 0
c[0].j = 0
c[0].k = 0
c[0].l = 0

*pint = 0x00000000 0000FF00 :

c[0].a = 0
c[0].b = 0
c[0].c = 0
c[0].d = 0
c[0].e = 0
c[0].f = 0
c[0].g = 1
c[0].h = 127
c[0].i = 0
c[0].j = 0
c[0].k = 0
c[0].l = 0


*pint = 0x00000000 00FF0000 :

c[0].a = 0
c[0].b = 0
c[0].c = 0
c[0].d = 0
c[0].e = 0
c[0].f = 0
c[0].g = 0
c[0].h = 32640
c[0].i = 0
c[0].j = 0
c[0].k = 0
c[0].l = 0

etc.

Forget portabilidad por un momento y asuma que se preocupan por una CPU, un compilador y un entorno de ejecución. ¿Por qué no puede VC ++ empacar esta estructura en 7 bytes? ¿Es cosa de longitud de palabra? El MSDN docs en #pragma pack dice " la alineación de un miembro será en un límite que es ya sea un múltiplo de n [1 en mi caso] o un múltiplo del tamaño del miembro, lo que sea menor ". ¿Alguien puede dar alguna idea de por qué me sale un sizeof 8 y no 7?

¿Fue útil?

Solución

MSVC ++ siempre asigna al menos una unidad de memoria que corresponde al tipo que utilizó para su campo de bits. Que utilizó unsigned int, lo que significa que un unsigned int se asigna inicialmente, y otro unsigned int se asigna cuando el primero se agota. No hay manera de forzar MSVC ++ para recortar la parte no utilizada de la segunda unsigned int.

Básicamente, MSVC ++ interpreta su unsigned int como una forma de expresar la requisitos de alineación para toda la estructura.

Uso tipos más pequeños para sus campos de bits (unsigned short y unsigned char) y reagrupar los campos de bits de forma que llenen la unidad asignada por completo -. De esa manera usted debería ser capaz de empacar las cosas lo más fuerte posible

Otros consejos

Campos de bits se almacenan en el tipo que se defina. Dado que está utilizando unsigned int, y no va a caber en un solo unsigned int entonces el compilador debe utilizar un segundo entero y almacenar los últimos 24 bits en el último número entero.

Bien está utilizando entero sin signo, que pasa a ser de 32 bits en este caso. El siguiente límite (a presión en el campo de bits) para unsigned int es de 64 Bit => 8 Bytes.

pst es correcto. El miembros están alineados en límites de 1 byte (o, más pequeño, ya que es un campo de bits). La estructura general tiene un tamaño de 8, y está alineado en un límite de 8 bytes. Esto cumple con el estándar tanto y la opción pack. Los documentos no dicen que habrá sin relleno al final.

Para dar otro interesante ilustra lo que está pasando, considere el caso en que desea empaquetar una estructura que cruza una frontera tipo. Por ejemplo.

struct state {
    unsigned int cost     : 24; 
    unsigned int back     : 21; 
    unsigned int a        :  1; 
    unsigned int b        :  1; 
    unsigned int c        :  1;
};

Esta estructura no puede ser embalado en 6 bytes utilizando MSVC por lo que yo sé. Sin embargo, podemos conseguir el efecto deseado de embalaje mediante la ruptura de los dos primeros campos:

struct state_packed {
    unsigned short cost_1   : 16; 
    unsigned char  cost_2   :  8;
    unsigned short back_1   : 16; 
    unsigned char  back_2   :  5;
    unsigned char  a        :  1; 
    unsigned char  b        :  1; 
    unsigned char  c        :  1; 
};

Esto puede de hecho ser embalado en 6 bytes. Sin embargo, el acceso al campo de costo original es extremadamente torpe y fea. Un método consiste en convertir un puntero a una estructura state_packed ficticia especializada:

struct state_cost {
    unsigned int cost     : 24;
    unsigned int junk     :  8; 
};

state_packed    sc;
state_packed *p_sc = &sc;

sc.a = 1;
(*(struct state_cost *)p_sc).cost = 12345;
sc.b = 1;

Si alguien conoce una forma más elegante de hacer esto, me gustaría saber!

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top