梦开始的地方 —— C语言(枚举+位段+联合体)
Posted 爱敲代码的三毛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了梦开始的地方 —— C语言(枚举+位段+联合体)相关的知识,希望对你有一定的参考价值。
位段
1. 什么是位段?
要想了解位段就得先学会使用结构体。文章链接——>详解结构体
位段的声明和结构体是十分类似的,它们有两个不同之处
- 位段的成员必须是
int
、unsigned int
、signed int
- 位段的成员名后面有一个冒号和数字。
定义一个名为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个比特位,从占10个比特位,加起来也就是17个比特位,4个字节就够了
- 而d需要30个比特位,不够则再开辟一个4个字节的空间
- 但d是使用前面剩余的一部分加上后面新开辟的空间,还是前面剩余的空间直接浪费使用新开辟的空间这就不得而知了
- C语言标准并没有规定剩余的空间是否一定要使用
2. 位段的内存分配
- 位段的成员变量可以是
int
、unsigned int
、signed int
、char
- 位段的空间上按照需要以4个字节(int),或者是1个字节(char)的方式来开辟的。
- 位段存在着很多不确定因素,它是不夸平台的,如果考虑可移植性就要避免使用位段。
举个列子,下面这位段是如何开辟内存的呢?
#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. 位段跨平台问题
前面说过位段的可移植性差,就是因为它的一些不确定因素
- int位段被当做有符号数还是无符号数是不确定的
- 位段中最大位的数目不能确定(6位机器最大16,32位机器最大32,写成27,在16位机器会出问题 )
- 位段中的成员变量在内存中分配空间,是从左向右分配还是从右向左分配,C语言标准也未定义。
- 当一个结构体包含两个位段,第二个位段成员比较大,前一个位段开辟空间剩余的比特位不够时,是浪费剩余的位,还是用掉剩余的再加上新开辟的空间,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;
以上是关于梦开始的地方 —— C语言(枚举+位段+联合体)的主要内容,如果未能解决你的问题,请参考以下文章