二 位段
结构体实现位段的能力
2.1 什么是位段
(1)位段的成员必须是 int、unsigned int 或signed int 。(还可以是char类型)
(2)位段的成员名后边有一个冒号和一个数字
1. #include <stdio.h> 2. struct A 3. { 4. int _a : 2;//_a需要2个比特位 5. int _b : 5;//_b需要5个比特位 6. int _c : 10;//_c需要10个比特位 7. int _d : 30;//_d需要30个比特位 8. }; //这就是一个位段 9. int main() 10. { 11. printf("%d\n", sizeof(struct A); 12. return 0; 13. }
运行结果为:8
上述代码位段,首先因为是int类型,所以开辟了4个字节(byte)(32个比特位)的空间,第一行用了2个比特位,第二行用了5个比特位,第三行用了10个比特位,此时还剩下15个比特位,但是第四行需要30个比特位,因为是int类型所以有开辟了4个字节。所以一共是8个字节。
对于是先用第一次开辟的剩下15个字节再用第二次开辟的32位中的15个比特位,还是直接用第二次开辟的空间的30个字节,这是C语言中没有定义的。
1. include <stdio.h> 2. 3. struct S 4. { 5. char a : 3; 6. char b : 4; 7. char c : 5; 8. char d : 4; 9. }; 10. 11. int main() 12. { 13. printf("%d\n", sizeof(struct S)); 14. return 0; 15. }
代码运行结果:3
在这里我们可以猜测VS2019,上一次开辟的空间剩下的不够用时,是被抛弃了,并没有用,直接用新开辟的空间。
2.2 位段的内存分配
1. 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
2. 位段的空间上是按照需要以 4 个字节( int )或者 1 个字节( char )的方式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段
1. struct S 2. { 3. char a : 3; 4. char b : 4; 5. char c : 5; 6. char d : 4; 7. }; 8. 9. int main() 10. { 11. struct S s = { 0 }; 12. s.a = 10; 13. s.b = 12; 14. s.c = 3; 15. s.d = 4; 16. return 0; 17. }
关于位段成员,怎么在内存中分配,C语言中没有明确的规定,要看编译器,VS2019就是上图所示。
2.3 位段的跨平台问题
1. int 位段被当成有符号数还是无符号数是不确定的。
2. 位段中最大位的数目不能确定。( 16 位机器最大 16 , 32 位机器最大 32 ,写成 27 ,在 16 位机器会出问题。(在早期16位平台上,sizeof(int)的大小是16bit,在当前32位平台和64位平台是32bit,当写的数字大于16,放在32位或者是64位平台上是有问题的)
3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是 舍弃剩余的位还是利用,这是不确定的。
总结:跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。
位段本身是不跨平台的。
三 枚举
枚举顾名思义就是列举,把可能的取值列举。(枚举是一个常量)
一周是从星期一到星期日,是有限的,可以一一列举出来。
3.1 枚举类型的定义
1. #include <stdio.h> 2. enum Day 3. { 4. //枚举的可能取值 5. Mon, 6. Tues, 7. Wed, 8. Thir, 9. Fri, 10. Sta, 11. Sun 12. }; 13. int main() 14. { 15. enum Day d = Sun; 16. printf("%d\n", Mon); 17. printf("%d\n", Tues); 18. printf("%d\n", Wed); 19. return 0; 20. }
打印的结构为:0 1 2;
以上 定义的的enum Day 就是一个枚举类型,{}中的内容是枚举类型的可能取值,叫做枚举常量。
这些可能取值都是有值的,默认从0开始,依次加一,也可以在定义的时候赋初值。
1. 2. #include <stdio.h> 3. 4. enum Day 5. { 6. //枚举的可能取值 7. Mon = 2, 8. Tues = 3, 9. Wed, 10. Thir, 11. Fri, 12. Sta, 13. Sun 14. }; 15. 16. int main() 17. { 18. enum Day d = Sun; 19. printf("%d\n", Mon); 20. printf("%d\n", Tues); 21. printf("%d\n", Wed); 22. return 0; 23. }
打印的结果为:2 3 4
1. #include <stdio.h> 2. 3. enum Day 4. { 5. //枚举的可能取值 6. Mon = 2, 7. Tues, 8. Wed = 3, 9. Thir, 10. Fri, 11. Sta, 12. Sun 13. }; 14. 15. int main() 16. { 17. enum Day d = Sun; 18. printf("%d\n", Mon); 19. printf("%d\n", Tues); 20. printf("%d\n", Wed); 21. return 0; 22. }
打印结果:2 3 3
枚举是一个常量,在定义枚举的时候后(无论有没有赋值),不可以对可能取值的值进行改变。例如:Mon= 3;(如果想让值发生改变,只能在定义枚举的时候进行赋值)
1. #include <stdio.h> 2. 3. enum Day 4. { 5. //枚举的可能取值 6. Mon, 7. Tues, 8. Wed, 9. Thir, 10. Fri, 11. Sta, 12. Sun 13. }; 14. 15. int main() 16. { 17. enum Day s = Mon;// 定义的变量只能是,枚举里面的元素,不能是数字 18. //只能用枚举常量给枚举变量赋值 19. printf("%d\n", Mon); 20. printf("%d\n", Tues); 21. printf("%d\n", Wed); 22. printf("%d\n", s);//0 23. printf("%d\n", sizeof(s));//因为是int类型,所以是4 24. return 0; 25. }
3.2 枚举的优点
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 防止了命名污染(封装)
4. 便于调试
5. 使用方便,一次可以定义多个常量
四 联合(共同体)
4.1 联合类型的定义
联合是一种特殊的自定义类型,这种类型定义的变量包含一系列的成员,特征是这些成员共用同一块空间。
1. #include <stdio.h> 2. 3. union Un 4. { 5. char c; 6. int i; 7. }; 8. 9. int main() 10. { 11. union Un u; 12. printf("%d\n", sizeof(u)); 13. printf("%p\n", &u); 14. printf("%p\n", &(u.c)); 15. printf("%p\n", &(u.i)); 16. return 0; 17. }
打印结果: 4 007D9BC 007D9BC 007D9BC
起始地址是一样的,共用一块地址,所以是4个字节。(但是i和c不能一起用这块地址)
联合的成员是共用同一块内存空间的,联合的大小至少是最少是最大成员的大小(因为联合至少得有能力保存最大的那个成员。
习题:判断当前计算机的大小端存储
知识点:低位放在低地址是小端,低位放在高地址是大端
常规写法:
1. #include <stdio.h> 2. 3. int cheak_sys() 4. { 5. int a = 1;//00 00 00 01(16进制) 6. return *((char*)&a); 7. } 8. 9. int main() 10. { 11. int ret = 0; 12. ret = cheak_sys(); 13. if (ret == 1) 14. printf("小端\n"); 15. else 16. printf("大端\n"); 17. return 0; 18. }
用联合的方法写:
1. #include <stdio.h> 2. 3. int cheak_sys() 4. { 5. union Un 6. { 7. char c; 8. int i; 9. }u; 10. u.i = 1; 11. return u.c; 12. } 13. 14. int main() 15. { 16. int ret = 0; 17. ret = cheak_sys(); 18. if (ret == 1) 19. printf("小端\n"); 20. else 21. printf("大端\n"); 22. return 0; 23. }
打印结果:小端
4.2 联合大小的实现
联合的大小至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
(和结构体一样)但是注意起始地址是一样的
1. #include <stdio.h> 2. union Un1 3. { 4. char c[5]; 5. int i; 6. }; 7. union Un2 8. { 9. short c[7]; 10. int i; 11. }; 12. int main() 13. { 14. printf("%d\n", sizeof(union Un1));//8 15. printf("%d\n", sizeof(union Un2));//16 16. return 0; 17. }
自定义类型到这里就结束了!!!