为了澄清我的问题,让我们从一个示例程序开始:
#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));
}
输出为“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);
}
可以预见,在使用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
等
暂时忘记可移植性并假设您关心一个CPU,一个编译器和一个运行时环境。为什么VC ++不能将这个结构打包成7个字节?这是一个字长的事吗? #pragma pack
上的MSDN docs表示“成员的对齐位于边界上,该边界是n [我的情况下为1]的倍数或成员大小的倍数,以较小者为准“。任何人都可以告诉我为什么我得到的尺码是8而不是7?
答案 0 :(得分:6)
MSVC ++始终至少分配一个与您用于比特字段的类型相对应的内存单元。您使用了unsigned int
,这意味着最初分配了unsigned int
,并且在第一个用尽时分配了另一个unsigned int
。没有办法强制MSVC ++修剪第二个unsigned int
的未使用部分。
基本上,MSVC ++会将您的unsigned int
解释为表达整个结构的对齐要求的方式。
为您的位字段(unsigned short
和unsigned char
)使用较小的类型并重新组合位字段,以便它们完全填充已分配的单元 - 这样您就可以将事物打包紧密尽可能。
答案 1 :(得分:3)
位域存储在您定义的类型中。由于您使用的是unsigned int
,并且它不适合单个unsigned int
,因此编译器必须使用第二个整数并将最后24位存储在最后一个整数中。
答案 2 :(得分:1)
你使用unsigned int,在这种情况下恰好是32位。 unsigned int的下一个边界(以适合位域)是64位=&gt; 8字节。
答案 3 :(得分:0)
pst是对的。 成员在1字节边界上对齐(或更小,因为它是位域)。整体结构的大小为8,并在8字节边界上对齐。这符合标准和pack
选项。文档从不说最后没有填充。
答案 4 :(得分:0)
为了给出另一个有趣的例子,请考虑您想要打包跨越类型边界的结构的情况。 E.g。
struct state {
unsigned int cost : 24;
unsigned int back : 21;
unsigned int a : 1;
unsigned int b : 1;
unsigned int c : 1;
};
据我所知,这个结构使用MSVC不能打包成6个字节。但是,我们可以通过分解前两个字段来获得所需的打包效果:
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;
};
这确实可以打包成6个字节。但是,访问原始成本字段非常笨拙和丑陋。一种方法是将state_packed指针强制转换为专用的虚拟结构:
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;
如果有人知道更优雅的方式,我很想知道!