Definition and use of bit fields

Bit field:  

When some information is stored, it does not need to occupy a complete byte, but only needs to occupy a few or one binary bit. For example, when storing a switch value, there are only two states of 0 and 1, and a binary digit can be used. In order to save storage space and make processing easy, C language provides a data structure called "bit field" or "bit segment". The so-called "bit field" is to divide the binary bits in a byte into several different areas, and specify the number of bits in each area. There is one domain name per domain, allowing operations by domain name in the program. In this way, several different objects can be represented by a binary bit field of one byte. Bit field members must be declared as int, unsigned int, or signed int (short char long).

1. Definition of Bit Field and Description of Bit Field Variable Bit field definition is similar to structure definition, and its form is:     

struct bit field structure name     
{ list of bitfields };  

The form of the bit field list is: Type specifier bit field: bit field length     
For example:     

struct bs     
{     
int a:8;     
int b:2;     
int c:6;     
};  

Bitfield variables are declared in the same way as structure variables. There are three ways to define and then explain, define and explain at the same time, or directly explain. E.g:     

  View Code

Note that data is a bs variable, occupying two bytes in total. The bit field a occupies 8 bits, the bit field b occupies 2 bits, and the bit field c occupies 6 bits. There are the following points for the definition of the bit field:     

1.  If the space left in a byte is not enough to store another bit field, the bit field should be stored from the next unit. It is also possible to intentionally start a bit field from the next cell. E.g:     

复制代码
struct bs     
{     
unsigned a: 4      
unsigned : 0  /* empty space */      
unsigned b: 4  /* store from the next unit */      
unsigned c: 4      
}     
复制代码

In this bit field definition, a occupies 4 bits of the first byte, the last 4 bits are filled with 0 to indicate that they are not used, b starts from the second byte and occupies 4 bits, and c occupies 4 bits.     

2. The length of the bit field cannot be greater than the length of the data type itself. For example, the int type can exceed 32 binary bits.

3.  The bit field can have no bit domain name, at this time it is only used for filling or adjusting the position. Unnamed bit fields cannot be used . E.g:     

复制代码
struct k     
{     
int a:1     
int :2 /*该2位不能使用*/     
int b:3     
int c:2     
};  
复制代码

从以上分析可以看出,位域在本质上就是一种结构类型, 不过其成员是按二进位分配的。     

二、位域的使用位域的使用和结构成员的使用相同,其一般形式为: 位域变量名.位域名 位域允许用各种格式输出。     

 

复制代码
struct bs     
{     
    unsigned a:1;     
    unsigned b:3;     
    unsigned c:4;     
} bit,*pbit;     
bit.a=1;     
bit.b=7; //注意:位域的赋值不能超过该域所能表示的最大值,如b只有3位,能表示的最大数为7,若赋为8,就会出错   
bit.c=15;

printf("%d,%d,%d/n",bit.a,bit.b,bit.c);
pbit=&bit;
pbit->a=0;
pbit->b&=3;
pbit->c=1;
printf("%d,%d,%d/n",pbit->a,pbit->b,pbit->c);
复制代码

上例程序中定义了位域结构bs,三个位域为a,b,c。说明了bs类型的变量bit和指向bs类型的指针变量pbit。这表示位域也是可以使用指针的。 

程序的9、10、11三行分别给三个位域赋值。( 应注重赋值不能超过该位域的答应范围)程序第12行以整型量格式输出三个域的内容。第13行把位域变量bit的地址送给指针变量pbit。第14行用指针 方式给位域a重新赋值,赋为0。第15行使用了复合的位运算符"&=", 该行相当于: pbit->b=pbit->b&3位域b中原有值为7,与3作按位与运算的结果为3(111&011=011,十进制值为 3)。同样,程序第16行中使用了复合位运算"=", 相当于: pbit->c=pbit->c1其结果为15。程序第17行用指针方式输出了这三个域的值。 

我们再来看看下面两个结构体定义:

复制代码
struct foo2 {
char    a : 2;
char    b : 3;
char    c : 1;
};

struct foo3 {
char    a : 2;
char    b : 3;
char    c : 7;
};
复制代码

我们来打印一下这两个结构体的大小,我们得到的结果是:
sizeof(struct foo2) = 1
sizeof(struct foo3) = 2
显然都不是我们期望的,如果按照正常的内存对齐规则, 这两个结构体大小均应该为3才对,那么问题出在哪了呢?首先通过这种现象我们可以肯定的是:带有'位域'的结构体并不是按照每个域对齐的,而是将一些位域 成员'捆绑'在一起做对齐的。以foo2为例,这个结构体中所有的成员都是char型的,而且三个位域占用的总空间为6 bit < 8 bit(1 byte),这时编译器会将这三个成员'捆绑'在一起做对齐,并且以最小空间作代价,这就是为什么我们得到sizeof(struct foo2) = 1这样的结果的原因了。再看看foo3这个结构体,同foo2一样,三个成员类型也都是char型,但是三个成员位域所占空间之和为9 bit > 8 bit(1 byte),这里位域是不能跨越两个成员基本类型空间的,这时编译器将a和b两个成员'捆绑'按照char做对齐,而c单独拿出来以char类型做对齐, 这样实际上在b和c之间出现了空隙,但这也是最节省空间的方法了。我们再看一种结构体定义:

struct foo4 {
char    a : 2;
char    b : 3;
int c : 1;
};

在foo4中虽然三个位域所占用空间之和为6 bit < 8 bit(1 byte),但是由于char和int的对齐系数是不同的,是不能捆绑在一起,那是不是a、b捆绑在一起按照char对齐,c单独按照int对齐呢?我们 打印一下sizeof(struct foo4)发现结果为8,也就是说编译器把a、b、c一起捆绑起来并以int做对齐了。就是说不够一个类型的size时,将按其中最大的那个类型对齐。此 处按int对齐。


C99规定int、unsigned int和bool可以作为位域类型,但编译器几乎都对此作了扩展,
允许其它类型类型的存在。
使用位域的主要目的是压缩存储,其大致规则为:

1) 如果相邻位域字段的类型相同,且其位宽之和小于类型的sizeof大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止
2) 如果相邻位域字段的类型相同,但其位宽之和大于类型的sizeof大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
3) 如果相邻的位域字段的类型不同,则各编译器的具体实现有差异,VC6采取不压缩方式,Dev-C++,GCC采取压缩方式;
4) 如果位域字段之间穿插着非位域字段,则不进行压缩;
5) 整个结构体的总大小为最宽基本类型成员大小的整数倍。
复制代码
struct s1 
{ 
int i: 8; 
int j: 4; 
int a: 3; 
double b; 
}; 

struct s2 
{ 
int i: 8; 
int j: 4; 
double b; 
int a:3; 
}; 

printf("sizeof(s1)= %d/n", sizeof(s1)); 
printf("sizeof(s2)= %d/n", sizeof(s2)); 
result: 16, 24 
复制代码


第一个结构体中,i,j,a共占15个位,不足8个字节,按double 8字节对齐,共16字节

第二个结构体中,i,j共占12位,不足8字节,按8字节对齐,a也按8字节对齐,加上double共8+8+8=24个字节

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325843670&siteId=291194637