C - 为什么#pragma pack(1) 将 6 位结构成员视为 8 位?
C - Why #pragma pack(1) Consider 6-bit struct member as an 8-bit?
我在定义 6-bit
字段并将其假定为 8-bit
时遇到了 #pragma pack(1)
错误行为。我阅读 this question 来解决我的问题,但它对我一点帮助都没有。
在 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)
?
#pragma pack
通常在字节边界而不是位边界上打包。这是为了防止在要压缩的字段之间插入填充 bytes。来自 Microsoft's documentation(因为您提供了 winapi
标签,并且我强调):
n
(optional) : Specifies the value, in bytes, to be used for packing.
当您试图让位域跨越字节边界时,实现如何处理位域是实现定义的。来自 C11 标准(第 6.7.2.1 Structure and union specifiers /11
节,再次强调):
An implementation may allocate any addressable storage unit large enough to hold a bitfield. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit. If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified.
More of the MS documentation 调用此特定行为:
Adjacent bit fields are packed into the same 1-, 2-, or 4-byte allocation unit if the integral types are the same size and if the next bit field fits into the current allocation unit without crossing the boundary imposed by the common alignment requirements of the bit fields.
在某些实现中,位域不能跨越变量边界。您可以在一个变量中定义多个位字段,前提是它们的总位数符合该变量的数据类型。
在您的第一个示例中,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
在您的第二个示例中,CHAR
中有足够的可用位来容纳所有 cChar1
...cChar4
,当它们每个都是 1 位时。因此,为什么 BASE64
的总大小是 1 个字节,而不是 4 个字节:
1 bit
+ 1 bit
+ 1 bit
+ 1 bit
- - - - - - - - - -
= 4 bits
= needs 1 byte
简单的回答是:这不是错误的行为。
打包试图以字节为单位放置单独的数据块,但它不能将两个 6 位块打包到一个 8 位字节中。所以编译器将它们放在不同的字节中,可能是因为访问单个字节来检索或存储 6 位数据比访问两个连续的字节并处理一个字节的一些尾部部分和另一个字节的一些前导部分更容易。
这是实现定义的,您对此无能为力。优化器可能有一个更喜欢大小而不是速度的选项——也许您可以使用它来实现您的预期,但我怀疑优化器会那么远。无论如何,大小优化通常会缩短代码,而不是数据(据我所知,但我不是专家,我在这里很可能是错的)。
我在定义 6-bit
字段并将其假定为 8-bit
时遇到了 #pragma pack(1)
错误行为。我阅读 this question 来解决我的问题,但它对我一点帮助都没有。
在 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)
?
#pragma pack
通常在字节边界而不是位边界上打包。这是为了防止在要压缩的字段之间插入填充 bytes。来自 Microsoft's documentation(因为您提供了 winapi
标签,并且我强调):
n
(optional) : Specifies the value, in bytes, to be used for packing.
当您试图让位域跨越字节边界时,实现如何处理位域是实现定义的。来自 C11 标准(第 6.7.2.1 Structure and union specifiers /11
节,再次强调):
An implementation may allocate any addressable storage unit large enough to hold a bitfield. If enough space remains, a bit-field that immediately follows another bit-field in a structure shall be packed into adjacent bits of the same unit. If insufficient space remains, whether a bit-field that does not fit is put into the next unit or overlaps adjacent units is implementation-defined. The order of allocation of bit-fields within a unit (high-order to low-order or low-order to high-order) is implementation-defined. The alignment of the addressable storage unit is unspecified.
More of the MS documentation 调用此特定行为:
Adjacent bit fields are packed into the same 1-, 2-, or 4-byte allocation unit if the integral types are the same size and if the next bit field fits into the current allocation unit without crossing the boundary imposed by the common alignment requirements of the bit fields.
在某些实现中,位域不能跨越变量边界。您可以在一个变量中定义多个位字段,前提是它们的总位数符合该变量的数据类型。
在您的第一个示例中,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
在您的第二个示例中,CHAR
中有足够的可用位来容纳所有 cChar1
...cChar4
,当它们每个都是 1 位时。因此,为什么 BASE64
的总大小是 1 个字节,而不是 4 个字节:
1 bit
+ 1 bit
+ 1 bit
+ 1 bit
- - - - - - - - - -
= 4 bits
= needs 1 byte
简单的回答是:这不是错误的行为。
打包试图以字节为单位放置单独的数据块,但它不能将两个 6 位块打包到一个 8 位字节中。所以编译器将它们放在不同的字节中,可能是因为访问单个字节来检索或存储 6 位数据比访问两个连续的字节并处理一个字节的一些尾部部分和另一个字节的一些前导部分更容易。
这是实现定义的,您对此无能为力。优化器可能有一个更喜欢大小而不是速度的选项——也许您可以使用它来实现您的预期,但我怀疑优化器会那么远。无论如何,大小优化通常会缩短代码,而不是数据(据我所知,但我不是专家,我在这里很可能是错的)。