C语言自定义类型【联合】和【枚举】详解
引言
详细介绍了联合和枚举是什么,各自的优缺点以及怎么使用
一、联合是什么
在 C 语言里,联合(
union
)是一种特殊的数据类型,它允许你在相同的内存位置存储不同的数据类型。与结构体(struct
)不同,结构体的每个成员都有自己独立的内存空间,而联合的所有成员共享同一块内存空间,这块内存大小由联合中最大的成员决定。
二、联合体的定义(声明)
像结构体一样,联合体也是由一个或者多个成员构成,这些成员可以不同的类型。 但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同⼀块内存空间。所以联合体也叫:共用体。
给联合体其中⼀个成员赋值,其他成员的值也跟着变化。
联合的定义和结构体类似,使用union
关键字,语法如下:
union union_name {
member_type1 member_name1;
member_type2 member_name2;
// 可以有更多成员
};
看一段代码,计算一下联合体类型的大小
#include <stdio.h>
//联合类型的声明
union Un
{
char c;
int i;
};
int main()
{
//联合变量的定义
union Un un = { 0 };
//计算连个变量的⼤⼩
printf("%d\n", sizeof(un));
return 0;
}
运行结果:
可以看到联合体 un 在内存中的占4个字节,int 是4 个字节,char 是1 个字节,所以最大开辟 4 个字节的空间。
三、联合体的特点
上面已经讲过了联合的所有成员共享同一块内存空间,这块内存大小由联合中最大的成员决定。
这里用具体代码来感受一下:
代码一:
#include <stdio.h>
//联合类型的声明
union Un
{
char c;
int i;
};
int main()
{
//联合变量的定义
union Un un = { 0 };
// 下⾯输出的结果是⼀样的吗?
printf("%p\n", &(un.i));
printf("%p\n", &(un.c));
printf("%p\n", &un);
return 0;
}
运行结果:会发现不同类型的地址是一样的,说明是开辟了一块空间。
代码二:
#include <stdio.h>
//联合类型的声明
union Un
{
char c;
int i;
};
int main()
{
//联合变量的定义
union Un un = { 0 };
un.i = 0x11223344;
printf("%x\n", un.i);
un.c = 0x55;
printf("%x\n", un.i);
return 0;
}
运行结果:
调试来看细节:
因为444在低地址出,所以 55 将 44 给覆盖了。
四、相同成员的结构体和联合体对比
struct S //结构体
{
char c;
int i;
};
struct S s = { 0 };
union Un //联合
{
char c;
int i;
};
union Un un = { 0 };
int main()
{
printf("%d\n", sizeof(s)); // 8
printf("%d\n", sizeof(un)); // 4
return 0;
}
再对比一下相同成员的结构体和联合体的内存布局情况。
五、联合体大小的计算
• 联合的大小至少是最大成员的大小。
• 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
#include <stdio.h>
union Un1
{
char c[5]; // 1 个字节 , 比8小 对齐数是1
int i; // 4 个字节 比8小 对齐数是4
};
union Un2
{
short c[7]; //2个字节 总共要占14bit 比8小, 对齐数是2
int i; // 4个字节 比8小 对齐数是4
};
int main()
{
//下⾯输出的结果是什么?
printf("%d\n", sizeof(union Un1)); // 8
printf("%d\n", sizeof(union Un2)); // 16
return 0;
}
关于对齐规则:可以看下这篇博文
C语言自定义类型【结构体】详解,【结构体内存怎么计算】 详解 【热门考点】:结构体内存对齐-CSDN博客
六、联合体的用途和注意事项
联合的用途
- 节省内存:当你需要在不同时刻使用不同类型的数据,且这些数据不会同时使用时,使用联合能节省内存。
- 数据解析:在处理二进制数据时,可利用联合按不同方式解释同一块内存。例如,把一个整数当作几个字节来处理。
注意事项
- 同一时间只能使用联合的一个成员,因为所有成员共享内存。
- 给一个成员赋值会覆盖其他成员的值。
七、联合体的一个练习题
写⼀个程序,判断当前机器是大端?还是小端?
int check_sys()
{
union
{
int i;
char c;
}un;
un.i = 1;
return un.c; // 返回 1 是小端,返回0是大端
}
int main()
{
int r = check_sys();
printf("%d", r);
return 0;
}
在vs2022上是小端字节序存储,所以会返回1
八、枚举是什么
在 C 语言里,枚举(
enum
)是一种用户自定义的数据类型,用于定义一组具有命名的整型常量。它让代码更加清晰易读,提高了代码的可维护性,因为可以用有意义的名字来代表特定的整数值。
九、枚举类型的定义(声明)
枚举类型的定义使用enum
关键字,语法如下:
enum enum_name {
constant1,
constant2,
// 可以有更多常量
};
在枚举定义里,每个常量都会被赋予一个默认的整数值,从 0 开始,后续常量的值依次递增。你也可以手动为常量指定值。
枚举顾名思义就是一一列举。
把可能的取值一一列举。
比如我们现实生活中:
一周的星期一到星期日是有限的7天,可以一一列举
性别有:男、女、保密,也可以一一列举
月份有12个月,也可以一一列举
三原色,也是可以意义列举
这些数据的表示就可以使用枚举了。
参考代码:
enum Day //星期 { Mon,//注意用,分割开 Tues, Wed, Thur, Fri, Sat, Sun }; enum Sex//性别 { MALE, FEMALE, SECRET }; enum Color//颜⾊ { RED, GREEN, BLUE };
以上定义的 enum Day , enum Sex , enum Color 都是枚举类型。
{}中的内容是枚举类型的可能取值,也叫 枚举常量。
这些可能取值都是有值的,默认从0开始,依次递增1,当然在声明枚举类型的时候也可以赋初值。
enum Color//颜⾊ { RED = 2, GREEN = 4, BLUE = 8 };
九、枚举类型的优点
我们可以使用 #define 定义常量,为什么非要使用枚举?
枚举的优点:
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 便于调试,预处理阶段会删除 #define 定义的符号
4. 使用方便,一次可以定义多个常量
5. 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用
十、枚举类型的使用
enum Color//颜⾊
{
RED = 1,
GREEN = 2,
BLUE = 4
};
enum Color clr = GREEN;//使⽤枚举常量给枚举变量赋值
那是否可以拿整数给枚举变量赋值呢?
在C语言中是可以的,但是在C++是不行的,C++的类型检查比较严格。