问题描述
为了澄清我的问题,让我们从一个示例程序开始:
#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\\\
,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);
}
可以预见,在x86_64上使用Visual Studio 2010,会发生以下情况:
* 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] 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
p>
暂时忘记可移植性,假设你关心一个CPU,一个编译器和一个运行时环境。为什么VC ++不能把这个结构打包成7个字节?它是一个字长的东西吗? ( #pragma) pack
说一个成员的对齐将在一个边界上,该边界是n的倍数(在我的例子中为1)或成员大小的倍数,以较小者为准。任何人都可以给我一些想法为什么我得到一个sizeof 8而不是7?
MSVC ++总是分配至少一个单位内存对应于您用于位字段的类型。你使用 unsigned int
,意味着最初分配一个 unsigned int
,另一个 unsigned int
在第一个用尽时分配。没有办法强制MSVC ++修剪第二个 unsigned int
的未使用部分。
基本上,MSVC ++将 unsigned int
解释为表示对齐要求
为位字段使用较小的类型(无符号short
和 unsigned char
)并重新组合位字段,以便它们完全填充分配的单元 - 这样,您应该能够尽可能紧密地包装。
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?
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.
这篇关于VC ++在打包位域时做什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!