打包位域时VC++在做什么?

Roo*_*oke 7 c++ visual-c++ bit-packing bit-fields

为了澄清我的问题,让我们从一个示例程序开始:

#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));
}
Run Code Online (Sandbox Code Playgroud)

输出为"8",这意味着我要打包的56位(7字节)被打包成8个字节,似乎浪费了整个字节.想知道编译器如何在内存中放置这些位,我尝试将特定值写入&c,例如:

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);
}
Run Code Online (Sandbox Code Playgroud)

可以预见,在使用Visual Studio 2010的x86_64上,会发生以下情况:

*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
Run Code Online (Sandbox Code Playgroud)

等等

暂时忘记可移植性并假设您关心一个CPU,一个编译器和一个运行时环境.为什么VC++不能将这个结构打包成7个字节?这是一个字长的事吗?在MSDN文档#pragma pack说"一个部件的对准将是一个边界要么是一个[在我的情况1]的n倍或构件中较小的大小的倍数上." 任何人都可以告诉我为什么我得到8号而不是7号?

AnT*_*AnT 6

MSVC++始终至少分配一个与您用于比特字段的类型相对应的内存单元.您使用过unsigned int,意味着a unsigned int最初是分配的,而另一个unsigned int是在第一个用尽时分配的.没有办法强制MSVC++修剪第二个未使用的部分unsigned int.

基本上,MSVC++将您解释unsigned int为表达整个结构的对齐要求的一种方式.

使用较小的类型为您的位字段(unsigned shortunsigned char)和重组位字段,以便他们填写完全的分配单元-这样你应该能够收拾东西尽可能紧.