目录
[4. 主要用途](#4. 主要用途)
一、联合体(共用体)
1.定义与使用
联合体允许在同一块内存空间中存储不同类型的数据。
与结构体不同,它的所有成员共享同一段内存,给联合体其中一个成员赋值,其他成员的值也跟着变化(因此也叫共用体)。
一个联合变量的大小,至少是最大成员的大小(因为至少得有能力保存最大的那个成员)。
#include <stdio.h>
union Data {
int i;
float f;
char str[20];
};
int main() {
union Data data;
data.i = 10;
printf("data.i = %d\n", data.i); // 输出 10
data.f = 220.5;
printf("data.f = %.1f\n", data.f); // 输出 220.5
// 注意:此时 data.i 的值已经被覆盖,变成无意义的内容
// 使用字符串成员
sprintf(data.str, "Hello");
printf("data.str = %s\n", data.str); // 输出 Hello
return 0;
}
2.核心特点:内存共享
#include <stdio.h>
//联合类型的声明
union Un
{
char c;
int i;
};
/******************************* 代码1 *******************************/
int main()
{
//联合变量的定义
union Un un = {0};
// 下⾯输出的结果是⼀样的吗?
printf("%p\n", &(un.i));
printf("%p\n", &(un.c));
printf("%p\n", &un);
return 0;
}
输出结果:
001AF85C
001AF85C
001AF85C
/******************************* 代码2 *******************************/
int main()
{
//联合变量的定义
union Un un = {0};
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n", un.i);
return 0;
}
输出结果:11223355

-
大小 :联合体的大小等于其最大成员 的大小(可能因对齐而稍大)。上面的例子中,假设
int4字节、float4字节、char[20]20字节,那么sizeof(union Data)通常是 20。 -
同一时间只有一个成员有效:写入一个成员会覆盖其他成员的值,因为它们在内存中起点相同。
-
访问 :用点运算符
.访问普通变量,用箭头->访问指针变量。
3.匿名联合体(c11)
可以在结构体内部直接定义一个没有名字的联合体,方便访问:
struct Variant {
int type;
union { // 匿名联合体
int i;
float f;
};
};
struct Variant v;
v.type = 1;
v.i = 100; // 直接访问,不需要额外的成员名
4. 主要用途
-
节省内存:当某个变量在不同时刻只需要存储不同类型的数据时(例如一个变量可能存整数或浮点数),用联合体比结构体省空间。
-
数据类型转换/查看底层表示 :比如通过把
float和unsigned int放在同一个联合体里,来读取浮点数的二进制表示。 -
硬件寄存器映射:在嵌入式编程中,常通过联合体将一个寄存器按整体或按位域来访问。
-
变体记录:实现类似"一个值有多种可能类型"的数据结构。
二、枚举
1.定义与使用
enum Weekday {
MON, // 默认值为 0
TUE, // 1
WED, // 2
THU, // 3
FRI, // 4
SAT, // 5
SUN // 6
};
// 定义枚举变量
enum Weekday today;
today = WED; // 相当于 today = 2;
/********************************** 其他创建方式 **********************************/
/* 1.手动赋值所有 */
enum Color {
RED = 1,
GREEN = 5,
BLUE = 10
};
/* 2.手动赋值第一个,后续自动递增 */
enum Status {
OK = 200,
CREATED, // 201
ACCEPTED, // 202
BAD_REQUEST = 400,
UNAUTHORIZED // 401
};
2.枚举与整数
在C语言中,枚举类型就是整数类型 ,枚举变量可以直接赋值给 int,也允许将 int 赋给枚举变量(虽然可能存在风险)。它不像C++那样有严格的类型检查。
enum Weekday day = WED;
int num = day; // 没问题,num = 2
day = 100; // 语法上允许,但逻辑上可能越界
3.枚举与#define
-
可读性 :
if (day == SAT)比if (day == 5)清晰得多。 -
调试友好:调试器通常能显示枚举常量的名字,而宏只是数字。
-
作用域控制 :枚举受作用域限制,可以放在结构体或函数内部,而
#define是全局的。 -
类型提示:虽然C不强制,但定义了枚举类型可以给程序员和工具更明确的意图。
4.匿名枚举
如果只是需要一组整数常量,可以不写类型名:
enum { MAX_SIZE = 100, MIN_SIZE = 10 };
int buf[MAX_SIZE];
5.常用场景
-
状态机(IDLE, RUNNING, STOPPED...)
-
选项标志(SMALL, MEDIUM, LARGE)
-
错误码(SUCCESS, ERROR_TIMEOUT, ERROR_INVALID...)
-
switch语句的 case 标签,比直接用魔术数字清晰得多。
三、补充
1.使用联合体判断大小端
int check_sys()
{
union
{
int i;
char c;
}un;
un.i = 1;
return un.c;//返回1是⼩端,返回0是⼤端
}
2.联合体与结构体内存对比
struct S
{
char c;
int i;
};
struct S s = {0};
union Un
{
char c;
int i;
};
union Un un = {0};

3.联合体占用空间大小的计算
- 联合的大小至少是最大成员的大小。
- 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
4.使用联合体节省空间举例
一个经典的例子是变体记录(Tagged Union)------用联合体来存储"根据类型不同而属性不同"的数据。比如在一个学生管理系统中,小学生、中学生、大学生有不同的专属信息,如果不使用联合体,你可能会把所有可能的属性都塞进一个结构体里,造成巨大浪费。
#include <stdio.h>
// 学生类型
#define TYPE_PRIMARY 0
#define TYPE_MIDDLE 1
#define TYPE_UNIVERSITY 2
struct Student {
char name[32];
int type;
// 所有类型可能用到的字段全都定义上
char primary_parent_phone[20]; // 小学生:家长电话
int middle_class_grade; // 中学生:班级排名
char university_major[30]; // 大学生:专业
int university_year; // 大学生:年级
};
/*
在这个结构体里,一个小学生根本不用的 university_major 也要占30个字节,
一个大学生不用的 middle_class_grade 也占4个字节。
总大小大约是 32 + 4 + 20 + 4 + 30 + 4 = 94 字节(再加上对齐填充,可能接近100字节)。
*/
#include <stdio.h>
#define TYPE_PRIMARY 0
#define TYPE_MIDDLE 1
#define TYPE_UNIVERSITY 2
struct Student {
char name[32];
int type;
union {
// 小学生专属数据
struct {
char parent_phone[20];
} primary;
// 中学生专属数据
struct {
int class_grade;
} middle;
// 大学生专属数据
struct {
char major[30];
int year;
} university;
} info; // 联合体变量
};
/*
此时,info 联合体的大小等于最大成员的大小(大学生那块,30+4=34 字节,加上可能的对齐),
整个结构体比之前少了至少24字节。
对于一个记录,节省可能不多,但如果有10万个学生的数组,就能节省约2.4 MB以上的内存。
*/
/************************************ 使用方法 ************************************/
void print_student(const struct Student *s) {
printf("姓名: %s\n", s->name);
switch (s->type) {
case TYPE_PRIMARY:
printf("家长电话: %s\n", s->info.primary.parent_phone);
break;
case TYPE_MIDDLE:
printf("班级排名: %d\n", s->info.middle.class_grade);
break;
case TYPE_UNIVERSITY:
printf("专业: %s, 年级: %d\n",
s->info.university.major,
s->info.university.year);
break;
}
}
int main() {
struct Student s = {
.name = "张三",
.type = TYPE_UNIVERSITY,
.info.university = {"计算机科学", 3}
};
print_student(&s);
return 0;
}