自定义类型详解(下)

简介: 自定义类型详解(下)

二 位段

结构体实现位段的能力

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. }

自定义类型到这里就结束了!!!

相关文章
|
5月前
|
存储 Linux C++
自定义类型讲解
自定义类型讲解
80 0
|
4月前
|
存储 编译器 Linux
自定义类型详解(1)
自定义类型详解(1)
36 5
|
4月前
自定义类型详解(2)
自定义类型详解(2)
41 1
|
5月前
|
存储 移动开发 API
其他内置类型
本文介绍了 .NET 中的 Console 类和 Environment 类。Console 类提供了控制台输入输出的功能,如设置背景色和前景色、打印文本、读取行和发出蜂鸣声。而 Environment 类则包含有关全局环境的信息和方法,如当前目录、进程路径、处理器数量、操作系统信息等。另外,文章还提及了 .NET Framework 的 AppDomain(用于表示应用程序域,但在 .NET Core 中功能减弱)和 .NET Core 中新引入的 AppContext 类,用于存储全局数据和开关。
|
5月前
|
编译器 Linux C++
自定义类型详解
自定义类型详解
|
5月前
|
编译器 C++
自定义类型
自定义类型
|
10月前
|
存储 算法 程序员
自定义类型总结
自定义类型总结
69 0
|
11月前
|
存储
自定义类型超详细解答!!!!!(下)
自定义类型超详细解答!!!!!
|
11月前
|
编译器 C++
自定义类型超详细解答!!!!!(上)
自定义类型超详细解答!!!!!
|
编译器 Linux C语言
自定义类型详解(上)
自定义类型详解(上)
自定义类型详解(上)