结构体实现位段(位段的填充&可移植性)
什么是位段
位段,C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”( bit field) 。利用位段能够用较少的位数存储数据。
位段和结构体其实是非常相似的,但是有两个不同点:
1. 位段的成员必须是 char、int、unsigned int
或signed int
。
2. 位段的成员名后边有一个冒号和一个数字。
举个例子
1. struct A 2. { 3. int _a : 2; 4. int _b : 5; 5. int _c : 10; 6. int _d : 30; 7. };
说明:
首先我们要明白位段中的这个“位”字其实指的是二进制位。
我们知道一个二进制位就是1个比特位。
所以,A中int _a : 2;
其实表示的就是
_a的大小是2bit
;
同理:
_b的大小是5bit
_c的大小是10bit
_d的大小是30bit
位段的内存分配
我们还是用上述代码,我们求一下上述代码的大小
我们惊奇的发现为8,占了8个字节,那么为什么是8呢?
1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
就是说,如果位段的成员全部是整型的(位段成员一般都是同类型的),那上去就先给这个位段开辟4个字节的空间,如果不够用,放不下所有的成员,那就再开辟4个字节的空间,还不够用,继续开辟,以此类推。如果成员全部是char类型的,那就一次开辟1个字节的空间,直至放得下所有成员。
具体分配为:
由于A的成员都是整型(int ),所以一次给A分配4个字节。4个字节是32给比特位,A的前3个成员_a、_b、_c占了17个bit,32-17还剩15bit,但是A的第四个成员_d大小是30bit,而15<30不够。怎么办?再分配4个字节,这下就能放下_d,因此,struct A的大小是4+4=8个字节。
注意:位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
位段的跨平台问题
1. int 位段被当成有符号数还是无符号数是不确定的。
2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机
器会出问题。
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是
舍弃剩余的位还是利用,这是不确定的
总结: 跟结构相比,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。
位段的应用
用于IP数据报,格式如下
枚举
枚举顾名思义就是一一列举。 把可能的取值一一列举。
比如我们现实生活中:
一周的星期一到星期日是有限的7天,可以一一列举。
性别有:男、女、保密,也可以一一列举。
月份有12个月,也可以一一列举
这里就可以使用枚举了。
接下来我们举个例子,比如:一星期有 7 天,如果不用枚举,我们需要使用 #define 来为每个整数定义一个别名:
1. #define MON 1 2. 3. #define TUE 2 4. 5. #define WED 3 6. 7. #define THU 4 8. 9. #define FRI 5 10. 11. #define SAT 6 12. 13. #define SUN 7
这个看起来代码量就比较多,接下来我们看看使用枚举的方式:
1. enum DAY 2. { 3. MON=1, TUE, WED, THU, FRI, SAT, SUN 4. };
这样看起来是不是更简洁了。
注意:第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。我们在这个实例中把第一个枚举成员的值定义为 1,第二个就为 2,以此类推。
枚举类型的定义
前面我们只是声明了枚举类型,接下来我们看看如何定义枚举变量。
我们可以通过以下三种方式来定义枚举变量
1、先定义枚举类型,再定义枚举变量
1. enum DAY 2. { 3. MON=1, TUE, WED, THU, FRI, SAT, SUN 4. }; 5. enum DAY day;
2、定义枚举类型的同时定义枚举变量
1. enum DAY 2. { 3. MON=1, TUE, WED, THU, FRI, SAT, SUN 4. } day;
3、省略枚举名称,直接定义枚举变量
1. enum 2. { 3. MON=1, TUE, WED, THU, FRI, SAT, SUN 4. } day;
枚举的优点
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 便于调试
4. 使用方便,一次可以定义多个常量
枚举的使用
示例一
1. #include <stdio.h> 2. 3. enum DAY 4. { 5. MON=1, TUE, WED, THU, FRI, SAT, SUN 6. }; 7. 8. int main() 9. { 10. enum DAY day; 11. day = WED; 12. printf("%d",day); 13. return 0; 14. }
输出结果为
示例二
在C 语言中,枚举类型是被当做 int 或者 unsigned int 类型来处理的,所以按照 C 语言规范是没有办法遍历枚举类型的。不过在一些特殊的情况下,枚举类型必须连续是可以实现有条件的遍历。
以下示例使用 for 来遍历枚举的元素:
1. #include <stdio.h> 2. 3. enum DAY 4. { 5. MON=1, TUE, WED, THU, FRI, SAT, SUN 6. } day; 7. int main() 8. { 9. // 遍历枚举元素 10. for (day = MON; day <= SUN; day++) { 11. printf("枚举元素:%d \n", day); 12. } 13. }
输出结果为
联合(共用体)
联合类型的定义
联合也是一种特殊的自定义类型
这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间(所以联合也叫共用体)。
比如:
1. //联合类型的声明 2. union Un 3. { 4. char c; 5. int i; 6. };
联合的特点
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联
合至少得有能力保存最大的那个成员)。
例如以下代码
1. union Un 2. { 3. char c; 4. int i; 5. }; 6. 7. int main() 8. { 9. printf("%d\n", sizeof(union Un)); 10. union Un un; 11. printf("%p\n", &un); 12. printf("%p\n", &(un.i)); 13. printf("%p\n", &(un.c)); 14. return 0; 15. }
我们再看一下运行结果
我们发现联合体大小为 4,而且&un,&(un.i),&(un.c)的地址相同,那么这样即可证明联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小·
联合体的应用
进行系统大小端的判断
代码如下
1. int check_sys() 2. { 3. union 4. { 5. int i; 6. char c; 7. }un = {.i = 1}; 8. return un.c; 9. } 10. 11. int main() 12. { 13. int ret = check_sys(); 14. 15. 16. if (ret == 1) 17. printf("小端\n"); 18. else 19. printf("大端\n"); 20. 21. return 0; 22. }
原理图如下:
博主所用的为小段存储模式,运行结果如下
总结
关于自定义类型就讲解到这儿,欢迎各位留言交流以及批评指正,如果文章对您有帮助或者觉得作者写的还不错可以点一下关注,点赞,收藏支持一下。