1. 联合体
1.1 联合体类型的声明
像结构体一样,联合体也是由一个或者多个成员构成,这些成员可以不同的类型。
但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同一块内存空间。所以联合体也叫:共用体。
给联合体其中一个成员赋值,其他成员的值也跟着变化
c
#include <stdio.h>
//联合类型的声明
union Un
{
char c;//占1字节
int i; //占4字节
};
int main()
{
//联合变量的定义
union Un un = {0};
//计算连个变量的⼤⼩
printf("%zd\n", sizeof(un));
return 0;
}
1.2 联合体的特点
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合体至少得有能力保存最大的那个成员)。
先来看一段代码:
c
#include <stdio.h>
union Un
{
char c;//占1字节
int i; //占4字节
};
int main()
{
union Un un = {0};
printf("%zd\n", sizeof(un));
printf("%p\n",&un);
printf("%p\n",&(un.i));
printf("%p\n",&(un.c));
return 0;
}
运行发现:

开辟了4个字节的空间 u 的地址/ u.i / u.c的地址是一样的 c 和 i 共用了一块空间 ,这样就导致对 c 修改会改变 i ,对 i 修改也是同理因为他们共用了一块空间所以同一时间只能有效使用一个成员。
总结一下联合体的特点:
1. 共用体大小 = 最大成员的大小(本例为 4 字节)
2. 共用体所有成员共享同一块内存,地址完全相同
3. 同一时间只能有效使用一个成员
1.3 相同成员的结构体和联合体对比
结构体:
c
#include <stdio.h>
struct S
{
char c;
int i;
};
struct S s = {0};

联合体:
c
#include <stdio.h>
union Un
{
char i;
int i;
};
union Un un = {0};

总结:

1.4 联合体大小的计算
计数规则:
联合的大小至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
c
#include <stdio.h>
union Un1
{
char c[5];//char类型占1字节 对齐数为1
int i; //int类型占4字节 对齐数为4
//联合的大小至少是最大成员(5)的大小
//对齐到最大对齐数(4)的整数倍
};
union Un2
{
short c[7];//short类型占2字节 对齐数为2
int i; //int类型占4字节 对齐数为4
//联合的大小至少是最大成员(14)的大小
//最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数(4)的整数倍
};
int main()
{
//下⾯输出的结果是什么?
printf("%d\n", sizeof(union Un1));
printf("%d\n", sizeof(union Un2));
return 0;
}
联合体的一个练习:
写一个程序,判断当前机器是大端?还是小端?
我们在之前的章节中实现过这个代码现在来回顾一下:
c
#include <stdio.h>
int check_sys()
{
int n = 1;
return *(char*)&n;
}
int main()
{
int ret = check_sys();//小端返回:1,大端返回:0
if(ret == 1)
printf("小端\n");
else
printf("大端\n");
return 0;
}
使用联合体实现:
c
#include <stdio.h>
int check_sys()
{
union Un
{
char c;
int i;
}u;
u.i = 1;
return u.c;
}
int main()
{
int ret = check_sys();//小端返回:1,大端返回:0
if(ret == 1)
printf("小端\n");
else
printf("大端\n");
return 0;
}
在小端模式下,整数 1 的低位字节(0x01)存储在低地址,而 char c 读取的正是这个低地址字节,因此返回 1
在大端模式下,整数 1 的高位字节(0x00)存储在低地址,char c 读取的是 0,因此返回 0
核心巧妙之处:利用联合体共享内存的特性
c 和 i 共用开头 1 个字节
给 i 赋值,直接能通过 c 读到第一个字节的数据
2. 枚举类型
2.1 枚举类型的声明
枚举顾名思义就是一个一个列举,把可能值全部列举出来。
比如:
一周的星期一到星期日是有限的7天,可以⼀⼀列举
性别有:男、女、保密,也可以一一列举
月份有12个月,也可以一一列举
c
#include <stdio.h>
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,当然在声明枚举类型的时候也可以赋初值。
c
enum Color//颜⾊
{
RED=2,
GREEN=4,
BLUE=8
};
2.2 枚举类型的使用
举例:计算器菜单选择程序,用枚举来表示菜单选项:
c
#include <stdio.h>
void menu()
{
printf("********************\n");
printf("*** 1.add 2.sub ***\n");
printf("*** 3.mul 4.div ***\n");
printf("*** 0.exit ***\n");
printf("********************\n");
}
enum Option
{
exit,//0
add,//1
sub,//2
mul,//3
div//4
};
int main()
{
int input = 0;
printf("请选择:>");
scanf("%d",&input);
switch (input)
{
case add: // 直接用枚举名,不用记数字!
break;
case sub:
break;
case mul:
break;
case div:
break;
case exit:
break;
default:
break;
}
return 0;
}
枚举的优点:
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 便于调试,预处理阶段会删除#define 定义的符号
4. 使用方便,一次可以定义多个常量
5. 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用