枚举和无符号长位域

Enum and unsigned long bitfields

如果我有以下带有枚举和无符号长整型的结构,填充是如何完成的?我相信编译器不会对 var1var2 做任何事情,因为它是 32 位并且已填充。

typedef struct {
    unsigned long var1 : 8;
    unsigned long var2 : 24;
    my_enum var3       : 2;
    my_enum var4       : 2;
} my_struct;

var3var4 都会有填充吗?例如 30 位填充到 var3 和 30 位到 var4,或 28 位。我只是想了解位域如何跨不同的数据类型和实现类型工作。

1. 位域中使用的类型:

我只会在您的结构中使用 signed intunsigned 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 longenum 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.


这对您的结构意味着什么:

var1var2将一起存储在4字节(32位)的内存中,没有任何间隙。因为位域可以在字节级操作,所以 var3var4 也可以存储在一起代表一个字节。但是它们的顺序是实现定义的。 如果在它们之间放置一个零长度的位字段,var4 将从下一个对齐的单元开始。您的结构的末尾可能会被填充到 32 位或 64 位。


您的结构的真实代码示例:

因为你不能获取位域的地址,所以分析位域的行为并不是那么容易,这里是我写的一小段代码,无论如何都要用一些变通方法来分析它。

我用易于识别的位模式设置结构内的值,并打印出整个结构的每一位(总大小 sizeof())。你可以看到result on ideone,或者下面。请注意,所使用的系统对每个结构变量使用小端格式,因此与人类可读形式(大端格式)相比,字节被交换。 var3var4 的顺序也不同,因为标准表示它是实现定义的。

输出:

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;
}