Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/147.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 什么是VC++;打包位字段时执行什么操作?_C++_Visual C++_Bit Fields_Bit Packing - Fatal编程技术网

C++ 什么是VC++;打包位字段时执行什么操作?

C++ 什么是VC++;打包位字段时执行什么操作?,c++,visual-c++,bit-fields,bit-packing,C++,Visual C++,Bit Fields,Bit Packing,为了澄清我的问题,让我们从一个示例程序开始: #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;

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

#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));
}
可以预见,在使用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个字节?它是一个字长的东西吗?on
#pragma pack
上说“成员的对齐将在一个边界上,该边界是n[1]的倍数或成员大小的倍数,以较小者为准。”谁能告诉我为什么我得到的大小是8而不是7?

位字段存储在您定义的类型中。由于您使用的是
无符号整数
,并且它不适合单个
无符号整数
,因此编译器必须使用第二个整数,并将最后24位存储在最后一个整数中。

您使用的是无符号整数,在这种情况下正好是32位。unsigned int的下一个边界(适合位字段)是64位=>8字节。

pst是正确的。成员在1字节边界上对齐(或更小,因为它是位字段)。整个结构的大小为8,并在8字节边界上对齐。这符合标准和
pack
选项。文档从不说末尾没有填充。

MSVC++总是至少分配一个与您用于位字段的类型相对应的内存单元。您使用了
unsigned int
,这意味着一个
unsigned int
最初被分配,而另一个
unsigned int
在第一个int耗尽时被分配。无法强制MSVC++修剪第二个
无符号int
中未使用的部分

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


为您的位字段使用较小的类型(
unsigned short
unsigned char
),并重新组合位字段,使它们完全填充分配的单元-这样您应该能够尽可能紧密地打包东西。

给出另一个有趣的示例,说明发生了什么,考虑要打包跨越类型边界的结构的情况。例如

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_压缩指针强制转换为专用的虚拟结构:

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;

如果有人知道更优雅的方法,我很想知道

文件上说“……将在……的边界上”;但是,我找不到关于大小保证的地方。在这种情况下,如果他需要15位和16位,他不能保存任何内容,他宁愿最后至少有9个字节,因为他已经使用了56位。不。。。有了这个建议,我真的可以把它打包成7个字节(谢谢AndreyT)。我不知道你说的9字节是什么意思。@Rooke:注意,
无符号字符位:10
是有效的,但可能不是你说的意思(最多只能存储8位值,并为填充保留2个额外的位)。也就是说,重新组合位字段以使每个位字段都适合所分配的单元是至关重要的。@David Rodríguez-dribeas:的确如此。解决方案要求对字段进行重新排序。问题不在于结尾处的填充,而在于位字段被压缩在位字段类型的单位内。该结构完全没有填充,只有两个
无符号int
成员。@David,该标准说(§6.7.2.1),“一个位字段被解释为一个有符号或无符号整数类型,由指定数量的位组成。[…]一个实现可以分配任何足以容纳一个位字段的可寻址存储单元。”所以我不认为
unsigned int
意味着它必须实际使用
unsigned int
作为存储单元,只是一些具有足够位的无符号类型。此外,位字段允许跨越存储单元边界。所以我确实认为结尾有填充物。顺便问一下,你看的是什么标准?目前的C++标准和C++ 0x FCD都没有第7.7.2.1节,部分标准至少对我来说是个混淆。我对该论点的理由是§9.6/1包含:常量表达式[位数]可能大于位字段类型的对象表示(3.9)中的位数;在这种情况下,额外位用作填充位,不参与位字段的值表示(3.9)。这意味着
无符号字符位:10
将无法存储10位整数(在8bit/char机器中),并且将不同于
无符号短整型位:10
,(假设为16位短整型)。根据这一点,我推断——我不能指出标准——如果编译器不能升级到更大的表示类型,那么它可能也不允许降级。此外,它还必须将表示类型从
int
中断为
short+char
,在这种特定情况下(再次假设为8位char,16位短,32位int),这是错误的。编译器可以用它想要的任何类型来存储位字段。@Abyx这与这里的其他答案和注释相反。如果你有什么东西支持你的主张,我们想看看。
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;