惧留孙网

juliusun.com

在线教程 > C语言教程 > 位域

位域

第 37/50 节 冥河C语言教程


结构体定义成员时,还有一般特殊的用法。结构体的成员可以不是占用某个系统预定义类型空间大小,而是占用自定义个数二进制比特位,这样的结构体成员,称为位域,也称位段。

本节的目的是普及一下位域的基础知识,不同的编译器实现方式和限制各不相同,实际开发时以使用的编译器实际支持为准。

位域成员定义方式如下:

类型 位域成员名 : 占用二进制比特位个数

例:


  1. struct TEST { //定义结构体
  2. int n : 1; //位域成员n为int类型,占用1位,只能表示0或1
  3. int m : 7; //位域成员m为int类型占用7位,
  4. char x : 3; //位域成员x占用3位
  5. };

注意,虽然位域成员自定义位数个数,但是也要确定位域成员类型,且位域成员占用位数不能多于类型的位数。

位域成员占用位数多少,会影响其值作用范围,使用时请注意。

例:


  1. struct TEST { //定义结构体
  2. char n : 9; //错误!,char类型占用一个字节,即8位,设置n占用9位错误
  3. };

位域成员存放规则如下:

    1、相邻位域成员类型相同,位数之和小于等于类型大小,则后面位域成员紧跟前一个,直到不能容纳为止。
    2、相邻位域成员类型相同,位数之和大于类型大小,后面成员跳过类型空间,从下一个相同类型空间开始存放。
    3、相邻位域成员类型不同,后一个成员跳过前一个成员类型,从下一个本成员类型空间开始存放(部分编译器优化,归类相同类型成员,不讨论)。


例:


  1. struct TEST //定义结构体
  2. int n : 15; //位域成员n为int类型,占用15位
  3. int m : 14; //与上一个类型相同,m占用14位加上n的15位共29位,小于int类型占4字节32位,所以与n放在同一个int单元内
  4. int x : 5; //与上一个类型相同,x占5位,前面相同类型已占用29位,之和为34位,多于32,从下一个int类型位置开始存放
  5. char y : 3; //与上一个类型不同,跳过int类型空间,从一个新的char类型位置开始存放
  6. };

位域成员可以没有成员名,叫空域,表示位数空闲不用。例:


  1. struct TEST { //定义结构体
  2. int n : 15; //位域成员n为int类型,占用15位
  3. int : 14; //此14位空闲不用,但是会占用内存
  4. int x : 5; //与上一个类型相同,x占5位,前面相同类型n占15+空域14=29位,32位的int空间放不下x,x从下一个int位置存放
  5. };

空域有一种特殊用法,占用0位,表示跳过此类型空间,后面的成员从下一个类型空间开始存放,就是进行对齐,例:


  1. struct TEST { //定义结构体
  2. int n : 15; //位域成员n为int类型,占用15位
  3. int : 0; //int类型占32位,n占15位,此空域表示下一个成员,跳过剩下的17位,新开辟一个类型
  4. int x : 5; //上一个为0位的空域表示填充整个int,x虽然与类型相同,且int除去n的15位有剩余,仍然要从下一个int存储
  5. };

注意,如果空域和上一个成员类型不同,则按两者占用空间较大的类型计算,例:


  1. struct TEST //定义结构体
  2. int n : 15;
  3. char : 0; //0位空域为char类型,n为int类型,int占32位,char占8位,按32位计算,n和0位空域共占32位
  4. int x : 5; //上面两个域按int计算,x会跳过上面的int,从下一个单元开始存放
  5. };

上面只按规则计算,不同的编译器规则可能有变,且可能存在优化,如开发时必须要用,请阅读所使用编译器的技术文档,本节只是初步介绍位域概念。

位域成员也可以与普通成员共同使用,普通成员总是跳过位域单元,如。


  1. struct TEST { //定义结构体
  2. int n : 15;
  3. int m: 3; //与上一个成员n一起填充在一个int单元内
  4. char name[20]; //正常成员,虽然上一个int没有填充满,但是仍然要跳过上一个int单元
  5. int x:5; //本域可能填充成员n和m所在的int单元,但是放在name正常成员后面,也要从新单元开始存储
  6. };

位域成员必须是基本类型,如int、char等,不能为数组、结构体等复杂类型。

不能通过取地址符获取位域成员地址,但是可以通过指向结构体的指针来引用位域成员,比如。


  1. struct TEST x,*p=&x;
  2. int *pi = &x.n; //错误,不能获取位域成员地址,它的地址不一定是字节对齐的
  3. p->n = 1; //正确,可以通过指向结构体的指针引用成员
  4. };

位域成员是编译器通过位运算计算而得,并不是真的在内存中按所说的规则存放,只不过对于开发者看来,像是真的自定义位数一样。

位域成员引用与正常成员引用方式相同,下面举一个简单的例子,定义一个结构体表示3盏不同颜色的灯的亮暗,1为亮,0为暗,代码:


  1. #include<stdio.h>
  2. int main( )
  3. {
  4. struct LAMP
  5. {
  6. char red : 1; //表示红灯,1位只能表示0或1
  7. char green: 1; //绿灯
  8. char yellow : 1; //黄灯
  9. };
  10. struct LAMP l = {1 ,1,0}; //赋值量像正常成员一样,按顺序给成员初始化
  11. if (l.red) //相当于if(l.read != 0)
  12. printf("红灯亮\n");
  13. else
  14. printf("红灯暗\n");
  15. if (l.green)
  16. printf("绿灯亮\n");
  17. else
  18. printf("绿灯暗\n");
  19. if (l.yellow)
  20. printf("黄灯亮\n");
  21. else
  22. printf("黄灯暗\n");
  23. getchar( ) ; //使程序暂停一下
  24. return 0;
  25. }

效果类似如下图:

Pic

运行平台不同效果图有所差异。

作者:冥河 QQ:3304576112
交流QQ群:554701039 C语言讲课群
本教程内容由本站保留版权,请勿复制传播
抖音
©2015-2024 惧留孙网 juliusun.com

京ICP备15039193号-1

首页 教程 下载 文章 聊天 我的