梦开始的地方 —— C语言(枚举+位段+联合体)


位段

1. 什么是位段?

要想了解位段就得先学会使用结构体。文章链接——>详解结构体

位段的声明和结构体是十分类似的,它们有两个不同之处

  1. 位段的成员必须是intunsigned intsigned int
  2. 位段的成员名后面有一个冒号和数字。

定义一个名为test的位段

struct test {
	int a : 2;
	int b : 5;
	int c : 10;
	int d : 30;
};
  • 冒号后面的数字表示该成员占几个比特位(注意有些平台有符号数的符号位也算一个比特位)
  • 比如在VS2019环境,a只有2个比特位00,第一位就是符号,那它只能表示-2~1的数字

那么这个位段test的大小是多少?

printf("%d\n", sizeof(struct test));

输出是8,也就是8个字节。

  • a占2个比特位,b占5个比特位,c占10个比特位,加起来也就是17个比特位,4个字节就够了
  • 而d需要30个比特位,不够则再开辟一个4个字节的空间
  • 但d是使用前面剩余的一部分加上后面新开辟的空间,还是前面剩余的空间直接浪费使用新开辟的空间这就不得而知了
  • C语言标准并没有规定剩余的空间是否一定要使用

2. 位段的内存分配

  1. 位段的成员变量可以是intunsigned intsigned intchar
  2. 位段的空间上按照需要以4个字节(int),或者是1个字节(char)的方式来开辟的。
  3. 位段存在着很多不确定因素,它是不夸平台的,如果考虑可移植性就要避免使用位段。

举个列子,下面这位段是如何开辟内存的呢?

#include <stdio.h>

struct test {

	char a : 3;
	char b : 4;
	char c : 5;
	char d : 4;
};
int main()
{
	struct test t = { 0 };
	t.a = 10;
	t.b = 12;
	t.c = 3;
	t.d = 4; 
	printf("%d\n", sizeof(struct test));
	return 0;
}
  • 把10赋值给a,a只有3个比特位,存放010
  • 把12赋值给b,b有4个比特位,存放1100
  • 把3赋值给c,c有5个比特位,存放00011
  • 把4赋值给d,d有4个比特位,在内存中存放0100

在这里插入图片描述

VS2019调试环境

在这里插入图片描述

通过上面的调试发现

  • 在VS2019平台
  • 前一个空间剩余的比特位不够时,直接浪费,使用新开辟空间的比特位
  • 一个字节内部,是从低位向高位使用的。

3. 位段跨平台问题

前面说过位段的可移植性差,就是因为它的一些不确定因素

  1. int位段被当做有符号数还是无符号数是不确定的
  2. 位段中最大位的数目不能确定(6位机器最大16,32位机器最大32,写成27,在16位机器会出问题 )
  3. 位段中的成员变量在内存中分配空间,是从左向右分配还是从右向左分配,C语言标准也未定义。
  4. 当一个结构体包含两个位段,第二个位段成员比较大,前一个位段开辟空间剩余的比特位不够时,是浪费剩余的位,还是用掉剩余的再加上新开辟的空间,C语言标准也是未定义的。

4. 位段的应用

这是ipv4首部的一张图,网络传输要保证正确的同时还要保证速度,把每一个字段精确的设计,可以避免没有必要的浪费,就能让数据包尽可能的小,提高传输效率。

在这里插入图片描述

联合(共用体)

1. 联合类型的定义

联合是一种特殊的自定义类型,它也和结构体类似,也可以包含一系列成员,特征是这些成员共用同一块内存空间,所以叫做联合也可以叫做共用体。

通过union关键字定义一个联合类型

#include <stdio.h>

union User
{
	int id;
	char ch;
};
int main()
{
	union User u;
    
	return 0;
}

2. 联合的特点

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

来看一段代码

#include <stdio.h>

union User
{
	int id;
	char ch;
};
int main()
{
	union User u;
	printf("%p\n", &(u.id));
	printf("%p\n", &(u.ch));


	return 0;
}

打印结果

006FF8B0
006FF8B0

发现这两个成员变量果然用的是同一块内存空间。

再来看一段代码验证一下

#include <stdio.h>

union User
{
	int id;
	char ch;
};
int main()
{
	union User u;
	u.id = 0x11223344;
	u.ch = 0x55;
	printf("%x\n", u.id);



	return 0;
}

打印结果

11223355
  • 因为我们这里是VS2019-X86环境,是小端存储
  • 联合的成员变量共用一块内存,那么ch在赋值的时候就会覆盖掉id的值

通过联合也可以判断大小端

  • 如果是小端,ch拿 的是低位的地址就能拿到1
  • 如果是大端,高位存低位,低位存高位,拿么ch拿低位拿到的就是0
#include <stdio.h>

union User
{
	int id;
	char ch;
};
int main()
{
	union User u;
	u.id = 1;
	if (u.id)
	{
		printf("小端\n");
	}
	else
	{
		printf("大端\n");
	}


	return 0;
}

3. 联合大小的计算

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

来看一个列子

#include <stdio.h>

union u1
{
	char arr[5];//5
	int a;//4
};
union u2
{
	short arr[7];//7
	int a;//4
};
int main()
{
	printf("%d\n", sizeof(union u1));//
	printf("%d\n", sizeof(union u2));//

	return 0;
}

打印结果

8
16
  • 联合中,数组大对齐数是它的成员对齐数,所以char arr[5]的对齐数是1
  • a是一个整形它的对齐数是4,所以该联合的最大对齐数是4
  • 但arr数组大小是5,当最大成员大小不是最大对齐数整数倍的时候,就要对齐到最大对齐数的整数倍
  • 所以对齐到最大对齐数4的整数倍8

联合类型u2

  • short arr[7],该数组大小是14,它的对齐数是数组中的成员也就是2
  • a的对齐数是4,而14不是4的倍数,要对齐到4的倍数16

所以联合的大小至少是最大成员的大小,但不一定是最大成员的大小,还要考虑到最大对齐数对齐

枚举

枚举:顾名思义就是一个一个列举。当我们有些类型,不适合用基本类型来,描述的时候就可以使用枚举。

1. 枚举的定义

通过enum关键字来定义枚举类型,用枚举来描述一个三原色

enum RGB
{
	RED,
	GREEN,
	BLUE
};

RGB就是自定义的枚举类型,枚举类型的成员是有初始值的,默认从0开始

#include <stdio.h>

enum RGB
{
	RED,
	GREEN,
	BLUE
};

int main()
{
	printf("%d\n",RED);
	printf("%d\n",GREEN);
	printf("%d\n",BLUE);


	return 0;
}

打印结果

1
2
3

当然也可以修改默认值

#include <stdio.h>

enum RGB
{
	RED = 10,
	GREEN = 15,
	BLUE
};

int main()
{
	printf("%d\n",RED);
	printf("%d\n",GREEN);
	printf("%d\n",BLUE);


	return 0;
}

打印结果

10
15
16

2. 为什么要使用枚举常量

枚举可以做到事情#define也可以做到,为什么要使用枚举?

枚举的一些优点

  • 增加代码的可维护性和可读性
  • 枚举具有类型检查,而#define没有
  • 定义方便,一次可以定义多个常量,而#define一次只能定义一个
  • 方便调试,#define是不能调试的,#define在预编译阶段就把对应的值给替换了
#include <stdio.h>

enum RGB
{
	RED = 10,
	GREEN = 15,
	BLUE
};

int main()
{
	enum RGB color = RED;
	printf("%d\n", color);


	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

爱敲代码的三毛

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

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

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

打赏作者

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

抵扣说明:

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

余额充值