问题
To clarify my question, let's start off with an example program:
#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));
}
The output is "8", meaning that the 56 bits (7 bytes) I want to pack are being packed into 8 bytes, seemingly wasting a whole byte. Curious about how the compiler was laying these bits out in memory, I tried writing specific values to &c
, e.g.:
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);
}
Predictably, on x86_64 using Visual Studio 2010, the following happens:
*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 portability for a moment and assume you care about one CPU, one compiler, and one runtime environment. Why can't VC++ pack this structure into 7 bytes? Is it a word-length thing? The MSDN docs on #pragma pack
says "the alignment of a member will be on a boundary that is either a multiple of n [1 in my case] or a multiple of the size of the member, whichever is smaller." Can anyone give me some idea of why I get a sizeof 8 and not 7?
回答1:
MSVC++ always allocates at least a unit of memory that corresponds to the type you used for your bit-field. You used unsigned int
, meaning that a unsigned int
is allocated initially, and another unsigned int
is allocated when the first one is exhausted. There's no way to force MSVC++ to trim the unused portion of the second unsigned int
.
Basically, MSVC++ interprets your unsigned int
as a way to express the alignment requirements for the entire structure.
Use smaller types for your bit-fields (unsigned short
and unsigned char
) and regroup the bit-fields so that they fill the allocated unit entirely - that way you should be able to pack things as tightly as possible.
回答2:
Bitfields are stored in the type that you define. Since you are using unsigned int
, and it won't fit in a single unsigned int
then the compiler must use a second integer and store the last 24 bits in that last integer.
回答3:
Well you are using unsigned int which happens to be 32 Bit in this case. The next boundary (to fit in the bitfield) for unsigned int is 64 Bit => 8 Bytes.
回答4:
pst is right. The members are aligned on 1-byte boundaries, (or smaller, since it's a bitfield). The overall structure has size 8, and is aligned on an 8-byte boundary. This complies with both the standard and the pack
option. The docs never say there will be no padding at the end.
回答5:
To give another interesting illustrates what's going on, consider the case where you want to pack a structure that crosses a type boundary. E.g.
struct state {
unsigned int cost : 24;
unsigned int back : 21;
unsigned int a : 1;
unsigned int b : 1;
unsigned int c : 1;
};
This structure can't be packed into 6 bytes using MSVC as far as I know. However, we can get the desired packing effect by breaking up the first two fields:
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;
};
This can indeed be packed into 6 bytes. However, accessing the original cost field is extremely awkward and ugly. One method is to cast a state_packed pointer to a specialized dummy struct:
struct state_cost {
unsigned int cost : 24;
unsigned int junk : 8;
};
state_packed sc;
state_packed *p_sc = ≻
sc.a = 1;
(*(struct state_cost *)p_sc).cost = 12345;
sc.b = 1;
If anyone knows a more elegant way of doing this, I would love to know!
来源:https://stackoverflow.com/questions/3919194/what-is-vc-doing-when-packing-bitfields