枚举和无符号长位域
Enum and unsigned long bitfields
如果我有以下带有枚举和无符号长整型的结构,填充是如何完成的?我相信编译器不会对 var1
和 var2
做任何事情,因为它是 32 位并且已填充。
typedef struct {
unsigned long var1 : 8;
unsigned long var2 : 24;
my_enum var3 : 2;
my_enum var4 : 2;
} my_struct;
var3
和 var4
都会有填充吗?例如 30 位填充到 var3
和 30 位到 var4
,或 28 位。我只是想了解位域如何跨不同的数据类型和实现类型工作。
1.
位域中使用的类型:
我只会在您的结构中使用 signed int
或 unsigned int
作为位域类型。根据 C99 standard (6.7.2.1 #4):
A bit-field shall have a type that is a qualified or unqualified version of
_Bool, signed int, unsigned int, or some other implementation-defined type.
当然 unsigned long
和 enum type
是实现定义的类型,但如果您使用它们,则行为不可移植。您可以在 2 个不同的 GCC 版本中看到它:
根据 gcc 4.8.4 的手册:
- Allowable bit-field types other than _Bool, signed int, and unsigned int (C99 6.7.2.1).
No other types are permitted in strictly conforming mode.
根据 gcc-5.2.0 的手册:
- Allowable bit-field types other than _Bool, signed int, and unsigned int (C99 and C11 6.7.2.1).
Other integer types, such as long int, and enumerated types are permitted even in strictly conforming mode.
2。
填充:
位域在字和字节级别工作,不能跨越字边界。 C99 保证位域将尽可能紧密地打包,前提是它们不跨越存储单元边界 (6.7.2.1 #10)。
An implementation may allocate any addressable storage unit large enough to hold a bit-
field. 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.
如果您想将一个单元中的现有位字段填充到下一个单元中,您可以使用标准 (6.7.2.1 #11) 中所述的长度为零的位字段来实现:
As a special case, a bit-field structure member with a width of 0
indicates that no further bit-field is to be packed into the unit in which the previous bit-
field, if any, was placed.
此外,它还说明了 C99 (6.7.2.1 #15) 的结尾:
There may be unnamed padding at the end of a structure or union.
这对您的结构意味着什么:
你var1
和var2
将一起存储在4字节(32位)的内存中,没有任何间隙。因为位域可以在字节级操作,所以 var3
和 var4
也可以存储在一起代表一个字节。但是它们的顺序是实现定义的。
如果在它们之间放置一个零长度的位字段,var4
将从下一个对齐的单元开始。您的结构的末尾可能会被填充到 32 位或 64 位。
您的结构的真实代码示例:
因为你不能获取位域的地址,所以分析位域的行为并不是那么容易,这里是我写的一小段代码,无论如何都要用一些变通方法来分析它。
我用易于识别的位模式设置结构内的值,并打印出整个结构的每一位(总大小 sizeof()
)。你可以看到result on ideone,或者下面。请注意,所使用的系统对每个结构变量使用小端格式,因此与人类可读形式(大端格式)相比,字节被交换。 var3
和 var4
的顺序也不同,因为标准表示它是实现定义的。
输出:
sizeof(my_struct) = 8
sizeof(my_struct_var) = 8
Byte 0: 1 0 0 0 0 0 0 1
Byte 1: 0 0 0 0 0 0 0 1
Byte 2: 0 0 0 0 0 0 0 0
Byte 3: 1 0 0 0 0 0 0 0
Byte 4: 0 0 0 0 1 1 1 0
Byte 5: 0 0 0 0 0 0 0 0
Byte 6: 0 0 0 0 0 0 0 0
Byte 7: 0 0 0 0 0 0 0 0
您的结构有效地使用了字节 0 到 3 和字节 4 的一半。因为我是在 64 位机器和 ideone 上编译的,所以半字节 4 被填充到字节 7。另外我建议阅读这个 guideline about structure padding and bitfields.
代码:
#include <stdio.h>
#include <limits.h>
#if CHAR_BIT != 8
#error "unsupported char size"
#endif
typedef enum { ONE, TWO, THREE } my_enum;
typedef struct
{
unsigned long var1 : 8;
unsigned long var2 : 24;
my_enum var3 : 2;
my_enum var4 : 2;
} my_struct;
int main(void)
{
int idx;
int bitIdx;
my_struct my_struct_var;
memset (&my_struct_var,
0,
sizeof(my_struct_var));
printf("sizeof(my_struct) = %lu\n", sizeof(my_struct));
printf("sizeof(my_struct_var) = %lu\n", sizeof(my_struct_var));
my_struct_var.var1 = 0x81; /* 1000 0001 */
my_struct_var.var2 = 0x800001; /* 1000 0000 0000 0000 0000 0001 */
my_struct_var.var3 = 0b10; /* 10 */
my_struct_var.var4 = 0b11; /* 11 */
for (idx = 0; idx < sizeof(my_struct_var); ++idx)
{
char * curByte = &my_struct_var;
curByte += idx;
printf("\nByte %d: ", idx);
for (bitIdx = 0; bitIdx < CHAR_BIT; ++bitIdx)
{
printf("%c ", ((*curByte & (1 << ((CHAR_BIT - 1) - bitIdx))) >> ((CHAR_BIT - 1) - bitIdx)) + '0');
}
}
return 0;
}
如果我有以下带有枚举和无符号长整型的结构,填充是如何完成的?我相信编译器不会对 var1
和 var2
做任何事情,因为它是 32 位并且已填充。
typedef struct {
unsigned long var1 : 8;
unsigned long var2 : 24;
my_enum var3 : 2;
my_enum var4 : 2;
} my_struct;
var3
和 var4
都会有填充吗?例如 30 位填充到 var3
和 30 位到 var4
,或 28 位。我只是想了解位域如何跨不同的数据类型和实现类型工作。
1. 位域中使用的类型:
我只会在您的结构中使用 signed int
或 unsigned int
作为位域类型。根据 C99 standard (6.7.2.1 #4):
A bit-field shall have a type that is a qualified or unqualified version of _Bool, signed int, unsigned int, or some other implementation-defined type.
当然 unsigned long
和 enum type
是实现定义的类型,但如果您使用它们,则行为不可移植。您可以在 2 个不同的 GCC 版本中看到它:
根据 gcc 4.8.4 的手册:
- Allowable bit-field types other than _Bool, signed int, and unsigned int (C99 6.7.2.1).
No other types are permitted in strictly conforming mode.
根据 gcc-5.2.0 的手册:
- Allowable bit-field types other than _Bool, signed int, and unsigned int (C99 and C11 6.7.2.1).
Other integer types, such as long int, and enumerated types are permitted even in strictly conforming mode.
2。 填充:
位域在字和字节级别工作,不能跨越字边界。 C99 保证位域将尽可能紧密地打包,前提是它们不跨越存储单元边界 (6.7.2.1 #10)。
An implementation may allocate any addressable storage unit large enough to hold a bit- field. 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.
如果您想将一个单元中的现有位字段填充到下一个单元中,您可以使用标准 (6.7.2.1 #11) 中所述的长度为零的位字段来实现:
As a special case, a bit-field structure member with a width of 0 indicates that no further bit-field is to be packed into the unit in which the previous bit- field, if any, was placed.
此外,它还说明了 C99 (6.7.2.1 #15) 的结尾:
There may be unnamed padding at the end of a structure or union.
这对您的结构意味着什么:
你var1
和var2
将一起存储在4字节(32位)的内存中,没有任何间隙。因为位域可以在字节级操作,所以 var3
和 var4
也可以存储在一起代表一个字节。但是它们的顺序是实现定义的。
如果在它们之间放置一个零长度的位字段,var4
将从下一个对齐的单元开始。您的结构的末尾可能会被填充到 32 位或 64 位。
您的结构的真实代码示例:
因为你不能获取位域的地址,所以分析位域的行为并不是那么容易,这里是我写的一小段代码,无论如何都要用一些变通方法来分析它。
我用易于识别的位模式设置结构内的值,并打印出整个结构的每一位(总大小 sizeof()
)。你可以看到result on ideone,或者下面。请注意,所使用的系统对每个结构变量使用小端格式,因此与人类可读形式(大端格式)相比,字节被交换。 var3
和 var4
的顺序也不同,因为标准表示它是实现定义的。
输出:
sizeof(my_struct) = 8
sizeof(my_struct_var) = 8
Byte 0: 1 0 0 0 0 0 0 1
Byte 1: 0 0 0 0 0 0 0 1
Byte 2: 0 0 0 0 0 0 0 0
Byte 3: 1 0 0 0 0 0 0 0
Byte 4: 0 0 0 0 1 1 1 0
Byte 5: 0 0 0 0 0 0 0 0
Byte 6: 0 0 0 0 0 0 0 0
Byte 7: 0 0 0 0 0 0 0 0
您的结构有效地使用了字节 0 到 3 和字节 4 的一半。因为我是在 64 位机器和 ideone 上编译的,所以半字节 4 被填充到字节 7。另外我建议阅读这个 guideline about structure padding and bitfields.
代码:
#include <stdio.h>
#include <limits.h>
#if CHAR_BIT != 8
#error "unsupported char size"
#endif
typedef enum { ONE, TWO, THREE } my_enum;
typedef struct
{
unsigned long var1 : 8;
unsigned long var2 : 24;
my_enum var3 : 2;
my_enum var4 : 2;
} my_struct;
int main(void)
{
int idx;
int bitIdx;
my_struct my_struct_var;
memset (&my_struct_var,
0,
sizeof(my_struct_var));
printf("sizeof(my_struct) = %lu\n", sizeof(my_struct));
printf("sizeof(my_struct_var) = %lu\n", sizeof(my_struct_var));
my_struct_var.var1 = 0x81; /* 1000 0001 */
my_struct_var.var2 = 0x800001; /* 1000 0000 0000 0000 0000 0001 */
my_struct_var.var3 = 0b10; /* 10 */
my_struct_var.var4 = 0b11; /* 11 */
for (idx = 0; idx < sizeof(my_struct_var); ++idx)
{
char * curByte = &my_struct_var;
curByte += idx;
printf("\nByte %d: ", idx);
for (bitIdx = 0; bitIdx < CHAR_BIT; ++bitIdx)
{
printf("%c ", ((*curByte & (1 << ((CHAR_BIT - 1) - bitIdx))) >> ((CHAR_BIT - 1) - bitIdx)) + '0');
}
}
return 0;
}