0

#pragma pack(1)在定义字段6-bit并将其假定为8-bit. 我读了这个问题来解决我的问题,但它根本没有帮助我。

Visual Studio 2012中,我定义struct了以下用于保存Base64字符:

#pragma pack(1)
struct BASE64 {
    CHAR    cChar1 : 6;
    CHAR    cChar2 : 6;
    CHAR    cChar3 : 6;
    CHAR    cChar4 : 6;
};

现在我得到了它的大小sizeof,但结果不是我所期望的:

printf("%d", sizeof(BASE64));      // should print 3

结果 :4

我期望得到3(因为6 * 4 = 24,所以24位是3字节)

事件我用1-bit字段测试它并得到正确的大小(1字节):

#pragma pack(1)
struct BASE64 {
    CHAR    cChar1 : 2;
    CHAR    cChar2 : 2;
    CHAR    cChar3 : 2;
    CHAR    cChar4 : 2;
};

实际上,为什么6-bit假设8-bit#pragma pack(1)

4

3 回答 3

6

#pragma pack通常打包在字节边界上,而不是位边界上。这是为了防止在要保持压缩的字段之间插入填充字节。微软的文档(因为你提供了winapi标签,我强调):

n(可选):指定用于打包的值(以字节为单位)。

当您尝试让它们跨越字节边界时,实现如何处理位字段是实现定义的。从 C11 标准(部分6.7.2.1 Structure and union specifiers /11,我再次强调):

实现可以分配任何大到足以容纳位域的可寻址存储单元。如果有足够的空间剩余,紧跟在结构中另一个位域之后的位域将被打包到同一单元的相邻位中。如果剩余空间不足,则将不适合的位域放入下一个单元还是与相邻单元重叠是实现定义的。单元内位域的分配顺序(高位到低位或低位到高位)是实现定义的。未指定可寻址存储单元的对齐方式。

更多 MS 文档指出了这种特定行为:

如果整数类型的大小相同并且如果下一个位字段适合当前分配单元而不跨越公共对齐要求强加的边界,则相邻位字段被打包到相同的 1、2 或 4 字节分配单元中位字段。

于 2018-08-21T07:18:20.953 回答
2

在一些实现中,位字段不能跨越变量边界。只有当它们的总位数适合该变量的数据类型时,您才能在变量中定义多个位字段。

在您的第一个示例中, a 中没有足够的可用位CHAR来容纳两者cChar1,并且cChar2当它们各自为 6 位时,因此cChar2必须进入CHAR内存中的下一个。与cChar3和相同cChar4。因此,为什么总大小BASE64是 4 个字节,而不是 3 个字节:

  (6 bits + 2 bits padding) = 8 bits
+ (6 bits + 2 bits padding) = 8 bits
+ (6 bits + 2 bits padding) = 8 bits
+ 6 bits
- - - - - - - - - - 
= 30 bits
= needs 4 bytes

在您的第二个示例中, a 中有足够的可用位CHAR来容纳所有cChar1...cChar4当它们每个为 1 位时。因此,为什么总大小BASE64是 1 个字节,而不是 4 个字节:

  1 bit
+ 1 bit
+ 1 bit
+ 1 bit
- - - - - - - - - - 
= 4 bits
= needs 1 byte
于 2018-08-21T07:20:55.027 回答
2

简单的答案是:这不是错误的行为

打包尝试将单独的数据块以字节为单位,但它不能将两个 6 位块打包到一个 8 位字节中。因此编译器将它们放在单独的字节中,可能是因为访问单个字节以检索或存储 6 位数据比访问两个连续字节并处理一个字节的一些尾随部分和另一个字节的一些前导部分更容易。

这是实现定义的,您对此无能为力。优化器可能有一个选项可以选择大小而不是速度——也许您可以使用它来实现您的预​​期,但我怀疑优化器会走得那么远。无论如何,大小优化通常会缩短代码,而不是数据(据我所知,但我不是专家,我很可能在这里错了)。

于 2018-08-21T07:31:09.080 回答