联合体的特点
像结构体一样,联合体也是一个或者多个成员构成的,这些成员可以是不同的类型
联合体的关键字:union
结构体的关键字:struct
枚举的关键字:enum
但是编译器只为最⼤的成员分配⾜够的内存空间。联合体的特点是所有成员共⽤同⼀块内存空间。所
以联合体也叫:共⽤体。
代码语言:javascript复制//struct S
//{
// char c;
// int i;
//
//};
//union Un
//{
// char c;
// int i;
//};
//int main()
//{
// printf("%zdn", sizeof(struct S));//8
// printf("%zdn", sizeof(union Un));//4
//
// return 0;
//}
/*
但是编译器只为最⼤的成员分配⾜够的内存空间。
联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共⽤体。
联合体的特点是所有成员共⽤同⼀块内存空间,这样一个联合变量的大小,至少是最大成员的大小
(因为联合至少的有能力保存最大的那个成员)
那么为什么这里是4呢?
*/
//union Un
//{
// char c;
// int i;
//};
//int main()
//{
// union Un un = { 0 };
// printf("%zdn", sizeof(union Un));
// printf("%pn", &un);//007EFDD4
把un里面的c和i的地址都打印出来
// printf("%pn", &(un.c));//007EFDD4
// printf("%pn", &(un.i));//007EFDD4
// return 0;
//}
/*
取出的地址都是相同的
第一个字节是c,所有的4个字节都是i
所以我们发现i和c公用这4个字节的空间
所以联合体也叫共用体
我们可以发现,当我们用i的时候我们就不能用c
用c的时候就不能用i
因为改i的时候,c也改了
所以联合体成员在使用的时候,一次只能用一个成员,不能同时一起用
同一个时间只能用一个成员
对于结构体的话,c和i有各自的空间,但是对于联合体来说,成员共用空间
*/
union Un
{
char c;
int i;
};
int main()
{
union Un un = { 0 };
un.i = 0x11223344;
un.c = 0x55;
//经过调试我们不难发现随着c的改变,i也被改变了
return 0;
}
联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共⽤体。
联合体大小的计算
联合的大小至少是最大成员的大小。
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍
代码语言:javascript复制//union Un
//{
// char arr[5];//对齐数是1
// //这个数组放在这里,跟放5个char类型是一样的
//
// int i;//对齐数是4
//};
//int main()
//{
// printf("%dn", sizeof(union Un));//8
// return 0;
//}
/*
计算出的是8,所以我们得知联合体的大小不一定是最大成员的大小
联合体的大小至少是最大成员大大小
这个联合体最大对齐数是4
那么联合体的总大小一定要是4的倍数
这个联合体最大成员的大小是这个数组,大小是5,就是相当于5个char类型
但是5不是4的倍数,所以后面还要浪费3个字节,对齐8,所以最终的大小是8个字节
所以联合体也是存在内存空间的对齐的
*/
//练习计算联合体大小
union Un
{
short arr[7];//对齐数是1
//这个数组放在这里,跟放5个char类型是一样的
int i;//对齐数是4
};
int main()
{
printf("%dn", sizeof(union Un));//16
return 0;
}
/*
因为shourt类型是2个字节,那么7个short就是14个字节了
short arr的对齐数是按照2来算的
i的对齐数是4,那么最大对齐数是4
那么联合体的大小必须是4的倍数
虽然说联合体很节省空间,但是也不是那么很绝对的节省空间
*/
联合体的运用
代码语言:javascript复制/*
图书:库存量、价格、商品类型、书名、作者、⻚数
杯⼦:商品类型、价格、库存量设计、
衬衫:设计、可选颜⾊、可选尺⼨、库存量、价格、商品类型
*/
//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。
所以我们就可以把公共属性单独写出来,剩余属于各种商品本⾝的属性使⽤联合体起来,这样就可以
介绍所需的内存空间,⼀定程度上节省了内存。
*/
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没有写名字,写成匿名,因为这些成员我们每次用的时候只用一次
联合体的练习
代码语言:javascript复制union Un
{
char c;//第一个字节
int i;
};
int main()
{
union Un un = {0};
un.i = 1;
if (un.c == 1)
{
printf("小端n");
}
else
{
printf("大端n");
}
return 0;
}
2.枚举类型
枚举类型的声明
枚举顾名思义就是一一列举的意思
就是将可能的值一一列举出来
我们可以声明枚举类型
枚举的关键字是enum
代码语言:javascript复制//struct A
//{
// int _a : 2;
// int _b : 5;
// int _c : 10;
// int _d : 30;
//};
//int main()
//{
// struct A sa = { 0 };
// //scanf("%d", &sa._b);//这是错误的
//
// //正确的⽰范
// int b = 0;
// scanf("%d", &b);
// sa._b = b;//直接进行赋值
// return 0;
//}
enum Day//星期
{
Mon,
Tues,
Wed,
Thur,
Fri,
Sat,
Sun
};
enum Sex//姓名
{
//该枚举类型的三种取值
//都是常量,被称为枚举常量
MALE=2,
FEMALE=4,//我们这里是给常量一个初始值,到后面就无法进行更改了
SECRET=8
};
int main()
{
//我们给枚举变量赋值的都是它的可能取值
/*enum Sex sex1 = MALE;
enum Sex sex2 = FEMALE;*/
printf("%dn", MALE);//0
printf("%dn", FEMALE);//1
printf("%dn", SECRET);//2
/*
打印出来的值是0 1 2
因为枚举常量的值默认是从0开始的,一次递增往下走,涨1
*/
//如果我们希望这个值是我们期望的,我们可以在枚举类型中进行更改
//假如我们仅仅只改变了第一个值为2
//那么剩下两个值就是3 4
//如果第1个值不赋值,第二个值赋值为8,那么打印出来的就是0 8 9
//从我们设置的值进行递增,第一个值不设置的话默认就是0
return 0;
}
枚举类型的优点
那么我们为什么使用枚举呢?
为什么使⽤枚举?
我们可以使⽤ #define 定义常量,为什么⾮要使⽤枚举?
枚举的优点:
- 增加代码的可读性和可维护性
- 和#define定义的标识符⽐较枚举有类型检查,更加严谨。
- 便于调试,预处理阶段会删除 #define 定义的符号
- 使⽤⽅便,⼀次可以定义多个常量
- 枚举常量是遵循作⽤域规则的,枚举声明在函数内,只能在函数内使⽤
enum Sex//姓名
{
MALE=2,
FEMALE=4,
SECRET=8
};
int main()
{
enum Sex sex1 = MALE;//因为MALE的类型是enum Sex类型的,所以这么进行赋值是对的
//enum Sex sex1 = 3;这么赋值就是错的,因为3的类型是整型,但是赋值的前面的枚举类型的
//因为类型是不一样的,所以我们不能进行赋值
return 0;
}
define定义的话是全局的定义的
枚举类型的使用
代码语言:javascript复制enum Color//颜⾊
{
RED=1,
GREEN=2,
BLUE=4
};
enum Color clr = GREEN;//使⽤枚举常量给枚举变量赋值
代码语言:javascript复制//写一个计算器---完成整数的加法、减法、乘法
enum Option
{
EXIT,//默认的值是0
ADD=1,//值表达的是1
SUB,
MULL,
DIV
};
int Add(int x, int y)
{
return x y;
}
int Sub(int x, int y)
{
return x - y;
}
int Mull(int x, int y)
{
return x * y;
}
int Div(int x, int y)
{
return x / y;
}
void menu()//菜单
{
printf("**********************************n");
printf("**********1. add 2. sub*******n");
printf("**********3. mull 4. div*******n");
printf("********* 0. exit **********n");
printf("**********************************n");
}
int main()
{
int input = 0,ret=0;
int x, y;
do
{
menu();
printf("请选择一个算法");
scanf("%d", &input);
switch (input)
{
case ADD://这么写的话,ADD的值还是表达的1
printf("请输入两个数");
scanf("%d %d", &x, &y);
ret=Add(x,y);
printf("%dn", ret);
break;
case SUB://对于这种我们想写什么就写什么case ADD都是可以的:
//我们是可以不用安排这个顺序的
printf("请输入两个数");
scanf("%d %d", &x, &y);
ret = Sub(x, y);
printf("%dn", ret);
break;
case MULL:
printf("请输入两个数");
scanf("%d %d", &x, &y);
ret= Mull(x, y);
printf("%dn", ret);
break;
case DIV:
printf("请输入两个数");
scanf("%d %d", &x, &y);
ret = Div(x, y);
printf("%dn", ret);
break;
case 0:
printf("退出n");
break;
default:
printf("选择错误,重新选择n");
break;
}
} while (input);
return 0;
}