目录
联合体(共用体)
联合体类型的声明和定义
像结构体⼀样, 联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。但是编译器只为 最大的成员分配足够够的内存空间。联合体的特点是所有成员共用同⼀块内存空间。 所以联合体也叫:共用体。给联合体其中⼀个成员赋值,其他成员的值也跟着变化。
union Un
{
char c;
int i;
};
int main()
{
//联合变量的定义
union Un un = { 0 };
//计算联合变量的大小
printf("%d\n", sizeof(un));
return 0;
}
运行结果:
为什么是4个字节呢?
首先我们前面已经说过了:联合体的成员是共用同一块空间的,这样一个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)
这个联合体有2个成员:char c 和 int i,c自身大小是1,i自身大小是4,因为它们共用同一块空间,并且这个联合体中最大默认对齐数也是4,所以分配四个字节就够了。
4个字节既可以放得下c ,也能放得下i
因为我们前面说过联合体的成员是共用同一块空间的,这要这么证明?
union Un
{
char c;
int i;
};
int main()
{
//联合变量的定义
union Un un = { 0 };
//判断下面输出的结果是一样的吗?
printf("%p\n", &(un.c));
printf("%p\n", &(un.i));
}
union Un
{
char c;
int i;
};
int main()
{
//联合变量的定义
union Un un = {0};
un.i = 0x11223344;
un.c = 0x55;
printf("%x\n", un.i);
}
运行结果:
相同成员的结构体和联合体的对比
我们再对比一下相同成员的结构体和联合体的内存布局情况。
联合体判断大小端
int main()
{
int n = 1;
int ret = *(char*)&n;
if (ret == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
}
当然啦,除了用指针的方式来做,我们还可以使用联合体来判断
union Un
{
char c;
int i;
};
int main()
{
union Un un = { 0 };
un.i = 1;
if (un.c == 1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
}
联合体大小的计算
我们在上面举的例子中的联合体大小就是它的最大成员的大小,但是不是所有的联合体大小都是这样呢?
不是的,联合体大小的计算其实也要考虑对齐的。
-
联合的大小至少是最大成员的大小。
-
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
union Un1
{
char c[5];
int i;
};
union Un2
{
short c[7];
int i;
};
int main()
{
//下面输出的结果是什么?
printf("%zd\n", sizeof(union Un1));
printf("%zd\n", sizeof(union Un2));
return 0;
}
运行结果:
联合体union Un1的的分析
1.首先第一个成员变量c是数组,占5字节
2.第二个成员变量i占4字
判断对齐数:
1.如果是数组的话,是按数组一个元素的大小来和默认对齐数来进行比较的,取它们中的最小值。因为数组c元素类型是char,占1字节,而默认对齐数是8,所以它的对齐数是1
2.i是int类型的,所以占4字节,而默认对齐数是8,所以它的对齐数是4
3.因为这个联合体成员变量中最大默认对齐数是4,而现在联合体所占大小是5字节,5不是4的倍数,所以还要向后占用3个字节的内存空间
联合体union Un2的的分析
1.首先第一个成员变量c是数组,占14字节
2.第二个成员变量i占4字
判断对齐数:
1.如果是数组的话,是按数组一个元素的大小来和默认对齐数来进行比较的,取它们中的最小值。因为数组c元素类型是short,占2字节,而默认对齐数是8,所以它的对齐数是2
2.i是int类型的,所以占4字节,而默认对齐数是8,所以它的对齐数是4
3.因为这个联合体成员变量中最大默认对齐数是4,而现在联合体所占大小是14字节,14不是4的倍数,所以还要向后占用2个字节的内存空间
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;//尺⼨
};
struct gift_list
{
int stock_number;//库存量
double price; //定价
int item_type;//商品类型
union un
{
//联合体中有三个成员变量
//匿名结构体变量book和匿名结构体变量mug和匿名结构体变量shirt
//它们三共用同一块空间
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;
};
创建相对应的变量(比如S1代表图书,S2代表衬衫),因为我们都有共用的属性,并且也有部分商品本身的属性,所以在结构体中创建联合体把部分属性共用起来就可以节省空间
注意:这个结构体要保存不同商品的信息,但是创建变量时我们是明确知道它要表示什么,所以用联合体来搭配就很不错
枚举
什么是枚举
枚举顾名思义就是⼀⼀列举。把可能的取值⼀⼀列举。比如我们现实生活中:
一周的星期一到星期日是有限的7天,可以一一列举
性别有:男、女、保密,也可以一一列举
月份有12个月,也可以一一列举
三原色,也是可以一一列举
枚举类型的定义
enum Gender
{
MALE,
FEMALE,
SECRET
};
使用枚举定义类型的注意事项:
枚举常量
enum Color
{
//这里列举enum Color的可能取值
RED,
GREEN,
BLUE
};
int main()
{
printf("RED=%d\n", RED);
printf("GREEN=%d\n", GREEN);
printf("BLUE=%d\n", BLUE);
return 0;
}
运行结果:
{}中的内容是枚举类型的可能取值,也叫枚举常量 。这些可能取值都是有值的,默认从0开始,依次递增1,当然在声明枚举类型的时候也可以赋初值。
这些枚举类型的可能取值也叫做枚举常量,那既然是常量,就意味这不能被修改
但是我们可以在定义的时候赋初始值
那如果只给第一个赋初值,就从该初值开始,还是依次增1。
枚举变量只能用枚举常量来赋值
enum Color
{
//这里列举enum Color的可能取值
RED = 1,
GREEN = 2,
BLUE = 3
};
int main()
{
enum Color c1 = RED;
printf("%d\n", c1);
return 0;
}
但是有人说,我就用常量来赋值,不用枚举常量赋值程序也可以通过啊
原因:这里没有报错是因为C语言语言检查不严格,用CPP也就是c++去测就会报错
枚举类型的优点
为什么要用枚举?
- 增加代码的可读性和可维护性
- 和#define定义的标识符比较枚举有类型检查,更加严谨。
- 便于调试,预处理阶段会删除 #define 定义的符号
- 使用方便,一次可以定义多个常量
- 枚举常量是遵循作业域规则的,枚举声明在函数内,只能在函数内使用
例子:
就像我们之前做的扫雷游戏
我们会做一个菜单,让用户在使用时根据菜单的提示做出相应的选择。
但是阅读者在看代码的时候,看到switch语句中case后面的值,可能还需要翻到上面看看它对应的是哪个功能。
但是,如果我们的程序已经完全写好之后,我们case语句对应的值,是不是就确定了
那这个时候,我们是不是就可以考虑用一个枚举呢?