在前面我们对 结构体进行了一个简单的介绍,本章就对 联合与枚举进行探讨。
1. 联合体
1.1 概念
在进行某些算法的C语言编程的时候,需要使几种不同类型的变量存放到同一段内存单元中。也就是使用覆盖技术,几个变量互相覆盖。这种几个不同的变量共同占用一段内存的结构,在C语言中,被称作“共用体”类型结构,简称共用体,也叫联合体。
联合体与结构体类似,也是由⼀个或者多个成员构成,成员也可以不同的类型,但是编译器只为最大的成员分配足够的内存空间。
1.2 联合体的定义
union 联合名
{
成员1;
成员2;
成员···;
成员n;
};
联合体的定义与结构体相同,可参考结构体章节。
1.3 联合体的特点
先观察一段代码:
#include<stdio.h>
struct S //结构体
{
int a;
char b;
};
union U //联合体
{
int c;
char d;
};
int main()
{
struct S s;
union U u;
printf("s=%d\n", sizeof(s)); //计算 s 的大小
printf("u=%d\n", sizeof(u)); //计算 u 的大小
return 0;
}
运行结果:
s=8 //不清楚的可以参考上一章(结构体)
u=4
最后显示联合体变量 u 的字节大小为 4 ,而整型加上字符型最小也是 5 ,所以联合体成员的存储方式并不是简单的相加。那么接下来就来观察一下成员的地址:
#include<stdio.h>
union U //联合体
{
int c;
char d;
};
int main()
{
union U u;
printf("u的地址:%p\n", &u); //计算 u 的大小
printf("c的地址:%p\n", (&u.c)); //计算成员 c 的大小
printf("d的地址:%p\n", (&u.d)); //计算成员 d 的大小
return 0;
}
运行结果:
u的地址:00000035F8F4F784
c的地址:00000035F8F4F784
d的地址:00000035F8F4F784
可以观察到联合体变量和成员的变量都相同,其实在上面概念中也提到过,所有成员共用同⼀块内存空间,还有重要的一点就是覆盖。
例(覆盖的表现):
#include<stdio.h>
union U
{
int a;
char b;
};
int main()
{
union U u;
u.a = 0x11223344; //赋16进制的值
u.b = 0x55; //赋16进制的值
printf("%x", u.a); //打印12进制整数
return 0;
}
运行结果:
11223355
图解:
1.4 联合体大小的计算
- 联合体大小至少是最大成员的大小。
- 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
也许用代码会更有说服力:
#include<stdio.h>
union U1
{
int a; //4个字节
char b[5]; //5个字节
};
union U2
{
int c; //4个字节
char d[9]; //9个字节
};
int main()
{
union U1 u1;
union U2 u2;
printf("u1=%d\n", sizeof(u1));
printf("u2=%d\n", sizeof(u2));
return 0;
}
运行结果:
u1=8
u2=12
在 u1 中,对齐数为 4 ,而联合体大小至少是最大成员的大小,同时还要是最大对齐数的整数倍,所以 u1 的大小就是 8 (4 的 2 倍);在 u2 中,数组占了 9 个字节,所以 8 显然是不够了,应扩大到最大对齐数(4)的三倍,也就是 12 。
不了解对齐数的可参考内存对齐规则
2. 枚举
2.1 概念
枚举是 C 语言中的一种基本数据类型,用于定义一组具有离散值的常量,它可以让数据更简洁,更易读。
枚举类型通常用于为程序中的一组相关的常量取名字,以便于程序的可读性和维护性。
定义一个枚举类型,需要使用 enum 关键字,后面跟着枚举类型的名称,以及用大括号 {} 括起来的一组枚举常量。每个枚举常量可以用一个标识符来表示,也可以为它们指定一个整数值,如果没有指定,那么默认从 0 开始递增。
简单来说就是一 一列举;
比如,一年的月份,周一到周日等
2.2 枚举的定义
enum 枚举名
{
枚举元素1;
枚举元素2;
……;
枚举元素n;
};
2.3 枚举的特点
#include<stdio.h>
enum day //枚举
{
MON,
TUE,
WED,
THU,
FRI,
SAT,
SUN
};
int main()
{
printf("%d %d %d %d %d %d %d", MON, TUE, WED, THU, FRI, SAT, SUN);
return 0;
}
运行结果:
0 1 2 3 4 5 6
我们可以再次发现如果没有指定整数值,那么默认从 0 开始递增。若为某个常量给定了整数值,那么就从给定了整数值的常量开始向后递增,其之前的则不变。
#include<stdio.h> enum day { MON, TUE, WED, THU=7, FRI, SAT, SUN }; int main() { printf("%d %d %d %d %d %d %d", MON, TUE, WED, THU, FRI, SAT, SUN); return 0; }
运行结果:
0 1 2 7 8 9 10
2.4 为什么使用枚举?
- 增加代码的可读性和可维护性
- 和#define定义的标识符比较枚举有类型检查,更加严谨。
- 便于调试,预处理阶段会删除 #define 定义的符号
- 使用方便,⼀次可以定义多个常量
- 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用
注意:在 C 语言中,允许给枚举变量赋值。而 C++ 中则不允许此行为。