结构体位制

一、位域的形式​​​​

      有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1 两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。所谓“位域”是把一个字节中的二进位划分为几 个不同的区域, 并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。 

注:因编译平台的不同,数据类型的长度也可能有异,以下int代表16Bit型(如果是32Bit类似,具体分析)。​

1、位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为:

struct 位域结构名

{ 位域列表 };

其中位域列表的形式为: 类型说明符 位域名:位域长度​


  • 基本概念
  •   struct _M​
  • {​
  •  (1) 类型 参数名 : 占位大小;

 (2) 类型 : 占位大小;​

}​

(1)类型 -- int,unsigned(32位),short,char。​

参数名 -- 同个结构体里面不能重名参数。

 占位大小 -- 不能大于类型最最大位数。

 (2) 一般用于寄存器中保留位

 例如:

struct bs

{

  int a:8;

  int b:2;

  int c:6;

};

位域变量的说明与结构变量说明的方式相同。 可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如:

struct bs

{

  int a:8;

  int b:2;

  int c:6;

}data;

说明data为bs变量,共占两个字节。其中位域a占8位,位域b占2位,位域c占6位。

 对于位域的定义尚有以下说明:​

(1)、一个位域必须存储在同一个字节中,不能跨两个成员变量类型。如一个成员变量所剩空间不够存放另一位域时,应从下一成员变量起存放该位域。也可以有意使某位域从下一成员变量开始。

例如:

struct bs

{

  int a:4;

  int :0;   

  int b:4;

  int c:4;

}

在这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。

2. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:

struct test

{

 unsigned char   a:1;

 unsigned char     :2; 

 unsigned char  b :3;

  unsigned char c :2;

};

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

 二、位域的使用

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

main()

{

  struct bs

  {

    unsigned char a:1;

    unsigned char b:3;

    unsigned char c:4;

  } bit,*pbit;

  bit.a=1;

  bit.b=7;

  bit.c=15;

  printf("%d,%d,%d ",bit.a,bit.b,bit.c);

  pbit=&bit;

  pbit->a=0;

  pbit->b&=3;

  pbit->c|=1;

  printf("%d,%d,%d ",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->c|1其结果为15。程序第17行用指针方式输出了这三个域的值。

为了节省空间,可以把几个数据压缩到少数的几个类型空间上,比如需要表示二个3位二进制的数,一个2位二进制的数,则可以用一个8位的字符表示之。​

三、汇总总结​

1、占位大小问题。

2、字节对齐问题。

3、特殊保留位0。

4、该结构体在内存中存放位置。​​

  • 占位大小问题:  
  • err -- int类型是32位,34大于32位,所以编译出错。

 struct _A {​

int x1:34;

 };​

err -- char类型是8位,9大于8位,所以编译出错。​

Struct _A1​

{

 char x1:9;

 };​

字节对齐​

 偏移量 -- 类型和占位大小影响(与结构体字节对齐差不多意思)​

struct B   ​

{  ​

 char x1:1;   //1位,字节对齐占1字节,8位。​

char x2:8;  ​

char x3:1;  ​

char x4:8;  ​

char x5:1;  

  • };  ​
  • struct C   
  • {  

char x1:4;  ​

char :1;  ​

char x2:3;  

  • };  
  •   
  • struct _C   
  • {  ​
  • char x1:4;  

char :8;  ​

char x2:3;  

  • };  ​
  • 特殊保留位0​
  • struct C1   
  • {  

char x1:4;  ​

char :0;  //这个0占了4个字节​

char x2:3;  

  • };  ​
  • struct_C1   
  • {  ​
  • char x1:4;  

char :4;   //这个0占了4个字节​

char x2:3;  ​

};  ​

内存中存放顺序​

struct D   ​

{  ​

char x1:1; //最低位

char x2:1;  

char x3:1;  

  • char x4:1;  
  • char x5:1;  
  • char x6:1;  
  • char x7:1;  
  • char x8:1;   //最高位​
  • };  ​
  • int main()  
  • {   
  •     //字节对齐

  B b;  ​

printf("sizeof(b) = %d\n", sizeof(b));  ​

C c;  ​

printf("sizeof(c) = %d\n", sizeof(c));  

_C _c;  ​

 printf("sizeof(_c) = %d\n", sizeof(_c));  ​

//特殊保留位0​

C1 c1;  ​

printf("sizeof(c1) = %d\n", sizeof(c1));  

memset(&c1, 0, sizeof(c1) );  ​

c1.x1 = 0xf;  ​

c1.x2 = 0x7;  ​

printf("c1 = 0x%x\n", c1);  ​

_C1 _c1;  ​

printf("sizeof(_c1) = %d\n", sizeof(_c1));  

memset(&_c1, 0, sizeof(_c1) );  

_c1.x1 = 0xf;  ​

_c1.x2 = 0x7;  ​

 printf("_c1 = 0x%x\n", _c1);  

  •   //内存中存放顺序

  D d;  

memset(&d, 0, sizeof(d) );  ​

printf("d = 0xx\n", d);  ​

d.x1 = 1;  

printf("d = 0xx\n", d);  ​

}  ​

四、例子​

  • 在使用结构体位制的时候有两点要特别注意:
  •   
  • 1.//位段成员的类型仅能够为unsigned或者int

  

  • 2.  unsigned b:4;  
  •      unsigned :0;    //定义长度为0的位段时不能指定名字,否则编译不过

     unsigned d:1;   //定义了0字段后,紧接着的下一个成员从下一个存储单元开始存放; 此例子中,d前面那个存储单元中的余下的27位中被0填充了


  • #include 

typedef struct _A  ​

{  

  •         unsigned int a:4;  //位段成员的类型仅能够为unsigned或者int​
  •         unsigned b:4;  
  •         unsigned c:2;  
  •         unsigned d:6;  
  •         unsigned E:1;  
  •         unsigned D:2;  
  •         unsigned T:3;  
  •         unsigned A:9;  
  •         unsigned h:4; //前面已经为31,故4+31>32已超过一个存储单元,所以4在一个新的存储单元存放

        unsigned y:29;//由于前面的4在一个新的存储单元的开头存放,且29+4>32, 故在另一个新的存储单元存放​

}A;               //所以最后求出的A的大小是4 + 4 + 4 =12​

/*对上面的具体解释: 一个位段必须存储在同一个存储单元中,不能跨两个单元.如果某存储单元空间中不能容纳​

下一个位段,则改空间不用,而从下一个存储单元起存放该位段. 结构体A中的h和y就是这种情况.在gcc环境下,测试后,一个存储单元为4个字节.​

typedef​

struct _S  

  • {  
  •         unsigned a:4;  
  •         unsigned b:4;  
  •         unsigned c:22;  
  •         unsigned q:1;  
  •         unsigned h:1;  
  •         //unsigned i:33;  // 错误:‘i’ 的宽度超过它自身的类型

  

  •         //unsigned i:1;当多出此行时,该结构体大小由4变为8,因为此行之前正好为32位

  } S;  ​

typedef struct _T  

  • {       //当没有占满一个存储单元时,结构体的大小对齐为一个存储单元的大小

        unsigned a:2;  

  •         unsigned b:2;  
  •         unsigned j:1;  
  •         unsigned : 1;//可以定义无名位段,此例中该无名位段占用1位的空间,该空间将不被使用

  } T;  ​

typedef struct _V  

  • {  
  •         unsigned a:1;  
  •         unsigned b:4;  
  •         unsigned :0;    //定义长度为0的位段时不能指定名字,否则编译不过

  

  •         unsigned d:1;   //定义了0字段后,紧接着的下一个成员从下一个存储单元开始存放;

  

  • }V;                                     //此例子中,d前面那个存储单元中的余下的27位中被0填充了

int main()  

  • {  
  •         A a; S s; T t; V v;  
  •         printf("sizeof(a)=%d\n", sizeof(a));  
  •         printf("sizeof(s)=%u\nsizeof(t)=%u\n", sizeof(s), sizeof(t));  
  •         printf("sizeof(v)=%d\n", sizeof(v));  
  •         return 0;  
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值