C语言自定义类型——位段、枚举、联合

 二、位段

2.1、位段的定义

位段只能在结构体中使用,主要用来节省空间。

当有一些成员的取值范围有限的情况下,需要的内存空间可以在一定程度上减少。

        位段的声明和结构是类似的,有两个不同:

        1.位段的成员必须是整型,比如int、unsigned int 或signed int 。

         2.位段的成员名后边有一个冒号和一个数字。

struct A
{
	int _a : 2;		//a只需要2个bit位
	int _b : 5;		//b只需要5个bit位
	int _c : 10;	//c只需要10个bit位
	int _d : 30;	//c只需要30个bit位
};
//47个bit位
//struct A大小是6个字节?

int main()
{
	printf("%d ", sizeof(struct A));	//8byte
	return 0;
}

 2.2、位段的内存分配

        位段的内存分配原则:

struct S
{
	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;		//char类型最多一个字节,8bit,所以位段最多就是8				
					//也就是冒号后面的数字不能超过前面的类型的大小
					//比如int类型,不能超过32
};

int main()
{
	struct S s = { 0 };	
	printf("%d \n", sizeof(struct S));

	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;

	return 0;
}

结构体S的位段分配之后,结构体S的大小是?有两种情况:

(1)先开辟8bit,a使用3bit,剩5bit,然后b再使用4bit,剩1bit,此时不够c使用,再开辟8bit,然后c使用第一次开辟剩下的1bit和第二个开辟的4bit,此时第二次开辟的空间剩4bit,而d刚好是4bit,位段逐个使用,总共16bit,所以是2byte;

(2)先开辟8bit,a先使用3bit,剩5bit,然后b再使用4bit,剩1bit,此时不够c使用,再开辟8bit,c优先使用新开辟的8bit,使用后剩3bit,此时第二次开辟的空间剩3bit,不够d使用,所以再开辟8bit,d优先使用新开辟的4bit,剩4bit,总共24bit,所以是3byte。

 通过在VS中分析,结构体S的大小是3byte,按照第二种方式分配空间。

分析:

结构体成员类型都是char类型,所以所以位段最多就是8bit,先开辟8bit空间供成员使用。

s.a=10,二进制为1010,位段只有3,所以存到空间中只有010;

s.b=12,二进制为1100,位段是4,所以存到空间中是1100;

s.c=3,二进制为0011,位段是5,所以存到空间中是00011;

第一次开辟的空间还剩1bit,不够用之后再开辟8bit,c优先使用新开辟的空间;

s.d=4,二进制为0100,位段为4,所以存到空间里是0100;

因为c使用第二次开辟的空间后,还剩3bit,不够d使用,所以再开辟8bit;

 最终在内存中,可以看到S在内存中存储的3byte是62 03 04。

2.3、位段的跨平台问题 

        位段的使用也会带来一些问题。

1、int位段被当成有符号数还是无符号数是不确定的。

2、位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题。

3、位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。

4、当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。 

 三、枚举

3.1、枚举定义

可以用枚举类型(enumerated type)声明符号名称来表示整型常量。

使用enum关键字,可以创建一个新“类型”并指定它可具有的值。(实际上,enum常量是int类型,因此,只要能使用int类型的地方就可以使用枚举类型)

//枚举类型的声明
enum spectrum
{
	red,	//枚举常量后面是逗号	    //0
	orange,		                    //1
	yellow,		                    //2
	green,		                    //3
	blue,		                    //4
	violet		                    //5
};
int main()
{
	//枚举变量的创建并初始化
	enum spectrum color = red;    //给枚举变量赋值时,
								  //用枚举常量的可能取值进行赋值,不能使用其他值

	printf("%d\n", sizeof(enum spectrum));	//4
	printf("%d\n", sizeof(color));			//4
	printf("%d\n", color);					//0
	return 0;
}

enum关键字;

spetrum作为标记名;

{  },花括号内的标识符枚举了spectrum变量可能有的值,称为枚举常量,枚举常量取值默认从0开始,依次向下递增,可以在定义的时候赋初值;

因此,color可能的值是red、orange、yellow等。

给枚举变量赋值时,用枚举常量的可能取值进行赋值,不能使用其他值。

enum DAY		
{
	Mon = 1,			//改变枚举常量的值,后面一样也是依次往下递增
	Tues,				//2
	Wed,				//3
	Thur,				//4
	Fri,				//5
	Sar,				//6
	Sun					//7
};

 3.2、枚举的优点

1、增加代码的可读性和可维护性;

2、和#define定义相比,枚举有类型检查,更加严谨;

3、防止了命名污染(封装);

4、便于调试;

5、使用方便,一次可以定义多个常量。

四、联合 

4.1、联合类型的定义

联合可以在不同的时刻,保存不同类型和长度的对象的变量。其提供了一种方式,它能在同一个内存空间中储存不同的数据类型(不是同时储存),也就是成员共用一块空间,所以联合也叫共用体。        

//联合类型的声明
union U
{
	char c;
	int i;
};

//联合变量的创建
union U u1;

int main()
{
	//联合变量创建
	union U u2;
	//计算联合变量的大小
	printf("%d\n", sizeof(union U));
	printf("%d\n", sizeof(u2));

	return 0;
}

4.2、联合的特点

联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。

//联合类型的声明
union Un
{
	int i;
	char c;
};
int main()
{
	//联合变量创建
	union Un un;

	printf("%p\n", &(un.i));	//地址
	printf("%p\n", &(un.c));	//地址
	
	un.i = 0x11223344;
	printf("%x\n", un.i);		//以16进制打印

	un.c = 0x55;
	printf("%x\n", un.i);
	printf("%x\n", un.c);

	return 0;
}

 与结构体的访问方式相同,联合访问方式也分为  点操作符  和   ->  。

即   联合名.成员    或者联合指针->成员     。

un.i和un.c的地址一样,说明它们两个共用同一块空间。

在对un.i 初始化后,其值为11223344;

然后对un.c赋值,因为两者共用相同的空间,调试的编译器是小端存储,所以un.c会在un.i的基础上重新赋值,55会将44覆盖。

 4.3、联合大小的计算

(1)联合的大小至少是最大成员的大小。

(2)当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。

联合也是存在内存对齐的。

//联合类型的声明
union U
{
	char c;
	int i;
};

//联合变量的创建
union U u1;

int main()
{
	//联合变量创建
	union U u2;
	//计算联合变量的大小
	printf("%d\n", sizeof(union U));	//4
	printf("%d\n", sizeof(u2));			//4

	return 0;
}

联合成员c的大小是1byte,对齐数为1;联合成员i的大小为4byte,对齐数为4;

所以联合的最大对齐数是4,联合的大小是4的倍数。

 联合成员c的大小是5byte,但是对齐数按照类型,所以对齐数是1;

联合成员i的大小是4byte,对齐数是4byte;

所以联合的对齐数为4,当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍,所以联合的大小要对齐到4的整数倍,8byte。

//联合类型的声明
union U
{
	char c[5];
	int i;
};

//联合变量的创建
union U u1;

int main()
{
	//联合变量创建
	union U u2;
	//计算联合变量的大小
	printf("%d\n", sizeof(union U));	//8
	printf("%d\n", sizeof(u2));			//8

	return 0;
}
  • 18
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值