文章目录
自定义类型
一、位段
1、位段概念
位段就是C语言允许一个结构体以比特位来指定其成员所占内存长度,这种以比特位为单位的成员是位段。
2、位段类型
类型可以是 int unsigned int signed int 或者是 char (属于整形家族)类型。
3、位段空间开辟使用和创建
- 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟。创建就像创建结构体一样,只是里面的成员变量不同。
- 成员的组成结构式是类型+变量名+冒号+比特位,这里的比特位是在所开辟空间里只能用那么多比特位。如果下一个比特位满足不了现有空间,那就再开辟一个某类型的空间
- 如下列各式及组成:
struct S
{
char a:5;
char b:4;
char c:4;
char d:3;
};
struct S s = {0};
s.a = 8;
s.b = 5;
s.c = 7;
s.d = 6;
4、位段变量具体如何占用空间
如下代码,看看a,b,c,d的:
struct S
{
char a:5;
char b:4;
char c:4;
char d:3;
};
struct S s = {0};
s.a = 8;
s.b = 5;
s.c = 7;
s.d = 6;
如图解析:
1、按定义的位段,先分配好所需比特位占的空间,a只能占用5个,b占用4个,c占用2个,d占用3个。
2、如果下一个所需比特位位数满足不了此空间,那就再开辟一个char类型空间
3、按刚才所分配好的空间,对赋的值进行截断或者不够补0:a=8,二进制位是1000,不够5个,就补0即01000;b=5,二进制位是101,不够4个,补0即0101;c=7,二进制位是111,所需比特位是2,需要截断,即11,;d=6,二进制位是110,整好是3个。
4、把这些比特放到刚才的空间里得到:0000 1000 0011 0101 0000 0110 按每组4位换成16进制是0x 08 35 06。
验证结果如下图:
5、位段优缺点
优点:
- 可以使数据单元节省储存空间,当程序需要成千上个数据单元时,这种方法就显得很重要。
- 位段可以很方便的访问一个整数值得部分内容从而简化程序源代码。
缺点:
- 位段中最大位的数目不能确定。16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。
- int 位段被当成有符号数还是无符号数是不确定的。
- 分配方向尚未定义:刚才的例子内存是从左向右分配的但是不能保证其他编译器是这种情况
- 不可移植:内存分配与内存对齐的实现方式依赖于具体的机器和系统,在不同的平台可能有不同的结果,导致位段是不可移植的。
- 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。钢刚才的例子是空间如果不够直接浪费掉,开辟下一个。
二、联合体
1、联合体概念
联合体所有成员都共用一块内存,使用的时候,这些数据在同一个内存位置,关键字是union。
2、联合体创建和性质
创建声明:
union Un//联合类型的声明
{
short a;
int i;
};
union Un un;//联合变量的定义
性质:
- 它们的成员共用同一块地址 。如下代码:
union Un
{
double i;
int a;
};
union Un un;
int main()
{
printf("%d\n", &(un.i));
printf("%d\n", &(un.a));
}
验证结果:
- 所站占空间大小至少是最大类型的字节数。
- 不会同时使用:因为是共用一块空间,用这个成员时不能用另一个成员,互相之间改变一个成员就会改变另一个。
3、计算联合体大小
计算联合体大小需同时满足如下条件:
- 联合的大小至少是最大成员的大小。
- 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
练习计算如下大小:
union Un1
{
char a[6];
int i;
};
union Un2
{
char b[9];
short i;
};
int main()
{
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));
return 0;
}
- 1、Un1中计算对齐数,char对齐数是1,int对齐数是4,最大对齐数是4。
2、最大成员大小是6。
3、最大成员大小不是最大对齐数的整数倍,要对齐,到8满足条件,所以Un1大小是8。 - 1、Un2中计算对齐数,char对齐数是1,short对齐数是2,最大对齐数是2。
2、最大成员大小是9。
3、最大成员大小不是最大对齐数的整数倍,要对齐,到10满足条件,所以Un1大小是10。
验证如图:
4、联合体判断主机大小端序
我们知道,数据在存储的时候超过一个字节,会涉及到顺序的问题。
分为两种:大端序和小端序。
- 大端序是低位放在高地址,高位放在低地址
- 小端序是高位放在高地址,低位放在地址
- 如图
有如下两种判断主机哪种存储方式
(1)、用联合体来判断
int check_sys()
{
union Un
{
char c;
int i;
}u;
u.i = 1;
return u.c;
}
int main()
{
int ret = check_sys();
if (ret == 1)
printf("小端\n");
else
printf("大端\n");
return 0;
}
解析:
1、联合体创建一字节的char类型和4字节int 类型。
2、把int类型i变量赋为1(便于判断。
3、1是4字节int类型,小端16进制是0x 01 00 00 00 ,大端16进制是0x 00 00 00 01,所以在check_sys()函数里直接返回1字节char c即u.c,因为它是一字节,如果c拿到的是01说明是小端,如果c拿到的是00说明是小端。
验证结果:
(2)、非联合体来判断
int check_sys()
{
int num = 1;
return *(char*)#
}
int main()
{
int ret = check_sys();
if (ret == 1)
printf("小端\n");
else
printf("大端\n");
return 0;
}
解析:
1、把int类型i变量赋为1(便于判断。
2、1是4字节int类型,小端16进制是0x 01 00 00 00 ,大端16进制是0x 00 00 00 01,所以在check_sys()函数里直接返回1取地址num,再强制转为char*类型,以便解引用拿到的是1字节的内容,如果c拿到的是01说明是小端,如果c拿到的是00说明是小端。
5、联合体优缺点
优点:
- 内存使用更为精细灵活,节省内存空间。
缺点: - 变量共存的,不能同时使用,不够包容。
三、枚举
1、枚举概念
C语言提供了一种枚举类型,能够列出所有可能的取值,也叫枚举常量,并给它们取一个名字,关键字是enum,专门来定义枚举类型。
例如:
一周的星期一到星期日是有限的7天,可以一一列举。
性别有:男、女、保密,也可以一一列举。
一年有12个月,也可以一一列举
2、枚举创建和性质
enum week//枚举类型的声明
{
Mo= 1,//枚举的可能取值
Tues,
Wed,
Th,
Fi,
Sa,
SU,
};
int main()
{
enum Week we = 5;//定义创建变量
printf("%d\n", Sa);
printf("%d\n", Th);
printf("%d\n", Wed);
printf("%d\n", Mo);
printf("%d\n", we);
return 0;
}
创建时里面的内容用逗号隔开。
性质:
这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值。
如下打印:
3、计算枚举大小
因为枚举的内容是未来可能的取值,要存的是一个值,而这个值是整型的存在,所以大小是4。平台不一样,大小也不一样。
enum Week
{
Mo,
Tues=5,
Wed,
Th,
Fi,
Sa,
SU,
};
int main()
{
printf("%d\n", sizeof(enum Week));
return 0;
}
验证如下
4、 枚举优点
-
- 增加代码的可读性和可维护性
-
- #define定义的标识符比较枚举有类型检查,更加严谨。
-
- 防止了命名污染(封装)
-
- 便于调试,#define不便于调试
-
- 使用方便,一次可以定义多个常量