位段、枚举、联合

位段

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

  • 位段的成员必须是intsigned intunsigned intchar(属于整形家族)
  • 位段的每个成员后边有一个冒号和一个数字(这个数字大小不能超过前面数据类型的大小)

下面是一个位段:

struct A
{
	int _a:2;
	int _b:5;
	int _c:10;
	int _d:30;
};

位段,位指的是二进制位。
位段的存在是为了:有的变量不需要太大的空间,有的时候两三个比特位就够了,这是就可以使用位段。


位段的内存分配

  • 位段的成员必须是intsigned intunsigned intchar(属于整形家族)
  • 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。

下面来看一下位段A的内存分配:

位段的成员是int,以4个字节开辟,先开辟4个字节也就是32比特位

  • 1.先分配_a_a占2比特位,此时4字节还剩30比特位,足够分配下一个的_b
  • 2.分配_b_b占5比特位,此时4字节还剩25比特位,足够分配下一个的_c
    *3. 分配_c_c占10比特位,此时4字节还剩15比特位,不够分配下一个的_d,跳过当前的剩下比特位,再会开辟4个字节,将_d存到下4个字节中
  • 4.分配_d_d占30个比特位,_d存到第二个4字节中

所以A占8字节空间大小

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

在这里插入图片描述

其实这8个字节是根据A中成员的情况,是同时开辟的,前面分析中的“先开辟”,“再开辟”等语句是用来分更好地去讲解位段内存分配的,并不是先后开辟的空间


位段变量内存的使用

struct S
{
char a:3;
char b:4;
char c:5;
char d:4;
};

int main()
{
	struct S s = {0};
	s.a = 10;
	s.b = 12;
	s.c = 3;
	s.d = 4;
}

由前面可知,S占3个字节,内存中开辟24个比特位

对于一串空间,是从左到右使用空间还是从右到左使用空间,C语言标准是没有定义的,接下来,我们假设从低位(高地址)到高位(低地址)使用(从右到左)

  • 先分配s.a = 10;10的二进制是1010,a只有3个比特位空间,所以截断,将010放到内存中,此时第一个1字节空间还有5比特位,够下面的b使用
  • s.b = 12,12的二进制是1100,b有4个比特位空间,将1100放到那5比特位中,第一个1字节空间·还有1比特位,放不下后面的c,所以这个剩下的1比特位浪费掉
  • s.c = 3,3的二进制是011,c有5个比特位空间,所以将00011放到第二个1字节空间里,还剩3比特位空间,不够后面的d,这3个比特位浪费掉
  • s.d = 4,4的二进制是0100,d占4个比特空间,所以将0100放到第3个1字节空间中

在这里插入图片描述

位段不存在内存对齐,因为位段的存在就是为了节省空间,如果还有内存对齐,反而还会浪费空间


位段的跨平台问题

  • nt 位段被当成有符号数还是无符号数是不确定的。
  • 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
  • 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

所以有跨平台需求时,不要使用位段。

跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在。


枚举

枚举就是把可能的取值一一列举

enum Sex//性别
{
	MALE,
	FEMALE,
	SECRET
}

上面定义的enum Sex就是枚举类型,大括号里的内容都是枚举类型可能的取值,叫做枚举常量

这些枚举常量类型都是int
如果在定义枚举时,没有对里面的枚举常量赋值,则默认从0开始,一次递增1

enum Sex
{
	MALE,
	FEMALE,
	SERCET
};

int main()
{
	printf("MALE:%d\n", MALE);
	printf("FEMALE:%d\n", FEMALE);
	printf("SERCET:%d\n", SERCET);
}

在这里插入图片描述
也可以在定义的时候也可以赋初值

enum Sex
{
	MALE = 4,
	FEMALE = 6,
	SERCET = 9
};

int main()
{
	printf("MALE:%d\n", MALE);
	printf("FEMALE:%d\n", FEMALE);
	printf("SERCET:%d\n", SERCET);
	return 0;
}

在这里插入图片描述
如果只对其中某个枚举常量赋初值,则后面的枚举常量的值也一次递增

enum Sex
{
	MALE = 4,
	FEMALE  ,
	SERCET 
};

int main()
{
	printf("MALE:%d\n", MALE);
	printf("FEMALE:%d\n", FEMALE);
	printf("SERCET:%d\n", SERCET);
	return 0;
}

在这里插入图片描述

对枚举变量赋值的时候尽可能用枚举常量赋值,如果用其他整形赋值的话会涉及强制转换,在C中会编译成功,而在C++中不会成功

int main()
{
	enum Sex s = MALE;//尽可能用可能取值去赋值
	return 0;
}

枚举的优点

我们可以使用#define定义常量,但是与枚举相比,还是枚举更好一点

枚举的优点:

  • 增加代码的可读性和可维护性
  • 和#define定义的标识符比较枚举有类型检查,更加严谨。
  • 防止了命名污染(封装)
  • 便于调试
  • 使用方便,一次可以定义多个常量

联合

联合又称为共用体,是一种特殊的自定义类型
这种类型定义的变量也包含一系列的成员,特征是这些成员公用同一块空间

union Un
{
char c;
int i;
};

上面的union Un就是一个联合

typedef union Un
{
	char c;
	int i;
}un;
int main()
{
	un u;
	printf("%p\n", &u);
	printf("%p\n", &u.c);
	printf("%p\n", &u.i);
	return 0;
}

输出结果是:
在这里插入图片描述
可以看见,u,u.c,u.i的地址都相同,就说明联合的成员是共用同一块内存空间的

联合un的储存形式如图:
在这里插入图片描述

一道面试题:
判断机器的大小端存储

我们可以通过联合,定义联合,里面有一个char类型,一个int类型,定义一个联合变量un,给un.i赋值1
因为联合共用一块内存空间,如果机器是小端存储,就会在首字节中存01,如果大端存储,就会在首字节中存储00,之后可以通过un,c取出首字节中的内容
进而可以判断是大端还是小端
在这里插入图片描述

int main()
{
	union
	{
		char c;
		int i;
	}un;
	un.i = 1;
	if (un.c == 1)
	{
		printf("小端存储");
	}
	else
	{
		printf("大端存储");
	}
}

联合大小的计算

  • 联合的大小至少是最大成员的大小
  • 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍

计算Un1的大小:

union Un1
{
	char c[5];
	int i;
};

int main()
{
	printf("%d", sizeof(union Un1));
}

Un1中有2个成员,一个是char c[5],一个是int i
字符数组中每个成员都是char,所以char c[5] 的对齐数是1,int i的对齐数是4。
此时最大成员大小是5,而对齐数是4,5不是4的整数倍,所以会浪费3个空间,是联合的大小扩大为8,是对齐数4的整数倍
输出结果:
在这里插入图片描述

计算Un2的大小:

union Un2
{
	short c[7];
	int i;
};
printf("Un2:%d\n", sizeof(union Un2));

short c[7]的对齐数是2,大小是14
int i的对齐数是4,大小是4
此时,最大成员大小是14,最大对齐数是4,14不是4的整数倍,所以联合大小应扩大为16
在这里插入图片描述

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯癫了的狗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值