1.联合体
1.1 联合体类型的声明
像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。
但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同⼀块内存空间。所以联合体也叫:共用体。
给联合体其中⼀个成员赋值,其他成员的值也跟着变化。
1.2联合体的特点
-
联合的成员是共用同⼀块内存空间的,这样⼀个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。
-
我们看如下代码,&un 和 &un.i 和 &un.c 的地址是同一个地址,我们可以得出char 和int 公用一个内存空间 4
union Un {
char c;
int i;
};
int main() {
union Un un = {0};
printf("%zd", sizeof(un));
printf("%zd", &un);
printf("%zd", &un.c);
printf("%zd", &un.i);
}
1.3联合体大小计算
-
联合的大小至少是最大成员的大小。
-
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
我们看如下代码 最大成员大小为 5 不是4的倍数 那就浪费三个字节 最后占8个字节
union Un {
char c[5];// 等于5个char类型 对齐数是1 8(最大) 1
int i;//3 4 8 4
//然后 5不是4的倍数 那就浪费三个字节 最后占8个字节
};
int main() {
printf("%zd", sizeof(union Un));
}
1.4什么场景使用联合体?
使用联合体是可以节省空间的,举例:
比如,我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯⼦、衬衫。
每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。
图书:书名、作者、页数
杯⼦:设计
衬衫:设计、可选颜色、可选尺存
那我们不思考,直接写出⼀下结构:
struct gift_list
{
//公共属性
int stock_number;//库存量
double price; //定价
int item_type;//商品类型
char title[20];//书名
char author[20];//作者
int num_pages;//⻚数
char design[30];//设计
int colors;//颜⾊
int sizes;//尺⼨
};
上述的结构其实设计的很简单,⽤起来也⽅便,但是结构的设计中包含了所有礼品的各种属性,这样使得结构体的大小就会偏大,比较浪费内存。因为对于礼品兑换单中的商品来说,只有部分属性信息是常用的。比如:商品是图书,就不需要design、colors、sizes。
所以我们就可以把公共属性单独写出来,剩余属于各种商品本⾝的属性使用联合体起来,这样就可以介绍所需的内存空间,⼀定程度上节省了内存。
用联合体类型的话,他们私有的属性就独立出来了,内存总共就给他们一个空间,然后他们共用一份空间,谁用的时候就占用这一份空间(使用的时候才创建),那就在一定程度上节省了空间
结构体用匿名类型
访问的时候 先找到gift_list结构体再找item 最后找你想要的
struct gift_list
{
int stock_number;//库存量
double price; //定价
int item_type;//商品类型
union {
struct
{
char title[20];//书名
char author[20];//作者
int num_pages;//⻚数
}book;
struct
{
char design[30];//设计
}mug;
struct
{
char design[30];//设计
int colors;//颜⾊
int sizes;//尺⼨
}shirt;
}item;
};
-
接下来我们回忆一下前面判断机器大小端的练习,现在我们用联合体去判断
union Un
{
char c;
int i;
};
int main() {
union Un u = { 0 }; //初始化
u.i = 1;
if (u.c == 1) {
printf("小端");
}
else
{
printf("大端");
}
}
2.枚举类型
2.1枚举类型声明
枚举顾名思义就是⼀⼀列举。
把可能的取值⼀⼀列举。
比如我们现实生活中:
⼀周的星期⼀到星期日是有限的7天,可以⼀⼀列举
性别有:男、女、保密,也可以⼀⼀列举 等等
这些数据的表示就可以使用枚举了。
{ }中的内容是枚举类型的可能取值,也叫 枚举常量 。
这些可能取值都是有值的,默认从0开始,依次递增1
//枚举类型
enum Sex {
MALE,
FEMALE,
SECRET
};
int main() {
enum Sex sex1 = MALE;
printf("%d\n", MALE);
printf("%d\n", FEMALE);
printf("%d\n", SECRET);
}
当然在声明枚举类型的时候也可以赋初值,第一个常量永远是0其他的一次递增
enum Sex {
MALE,
FEMALE=2,
SECRET=
};
int main() {
enum Sex sex1 = MALE;
printf("%d\n", MALE);
printf("%d\n", FEMALE);
printf("%d\n", SECRET);
}
-
我们会疑惑,为什么常量可以修改呢? 这并不是修改,只是给一个初始值,我们可以看到以下的赋值语句 MALE = 5 报错
//枚举类型
enum Sex {
MALE,
FEMALE=2,
SECRET=3
};
int main() {
enum Sex sex1 = MALE;
MALE = 5; //这里不能赋值成功 修改失败
printf("%d\n", MALE);
printf("%d\n", FEMALE);
printf("%d\n", SECRET);
}
2.2 枚举类型的优点
为什么使用枚举?
我们可以使用 #define 定义常量,为什么非要使用枚举?
枚举的优点:
增加代码的可读性和可维护性
和#define定义的标识符比较枚举有类型检查,更加严谨。
便于调试,预处理阶段会删除 #define 定义的符号
使用方便,⼀次可以定义多个常量
枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用
下面举一个加法器的例子体现枚举类型的优点,定义一个枚举类型可以让代码更有可读性
//定义一个枚举类型 提高代码的可读性
enum Option {
EXIT = 0,
ADD,
SUB,
MUL,
DIV
};
void menu() {
printf("***********************************");
printf("********1.add 2.sub**************");
printf("********3.mul 4.div**************");
printf("******** 0.exit **************");
}
int main() {
int input = 0;
do {
menu();
printf("请选择你想要的计算:");
scanf("%d", &input);
switch (input)
{
case ADD: //方法名 比 1, 2 ,3 更有可读性,便于理解
Add();
break;
case SUB:
Sub();
break;
case MUL:
Mul();
break;
case DIV:
Div();
break;
case EXIT:
break;
default:
printf("选择有误,请重新选择");
}
} while (input);
}