【C语言】19. ⾃定义类型:联合和枚举
文章目录
- 一、联合体
- 1、联合体类型的声明
- 2、联合体的特点
- 3、结构体和联合体的对⽐
- 4、联合体⼤⼩的计算
- 5、练习
- 二、枚举
- 1、枚举类型的声明
- 2、枚举类型的使用
- 3、枚举类型的优点
一、联合体
1、联合体类型的声明
像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以是不同的类型。
但是编译器只为最⼤的成员分配⾜够的内存空间。联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共⽤体。
因此如果给联合体其中⼀个成员赋值,其他成员的值也会跟着变化。
关键词为union。
接下来我们来计算一个联合体的大小:
#include<stdio.h>//联合体的声明
union Un
{char c;int i;
};int main()
{union Un un = { 0 };printf("%zd\n", sizeof(un));return 0;
}
运行结果:
可以看到结果为4,那么这个结果是怎么得出的呢?我们就要了解一下联合体的特点了。
2、联合体的特点
联合体的成员是共⽤同⼀块内存空间的,这样⼀个联合变量的⼤⼩,⾄少是最⼤成员的⼤⼩。
我们来观察一段代码:
#include<stdio.h>union Un
{char c;int i;
};
int main()
{union Un un = { 0 };printf("%p\n", &un);printf("%p\n", &(un.i));printf("%p\n", &(un.c));return 0;
}
运行结果:
可以看到联合体的成员地址都是一样的,说明他们的确是共用一块内存空间。
再观察一段代码:
#include<stdio.h>union Un
{char c;int i;
};int main()
{union Un un = { 0 };un.i = 0x11223344;un.c = 0x55;printf("%x\n", un.i);return 0;
}
调试观察:
可以看到我们将变量 i 的第四个字节的内容改为55,同样说明了联合体的成员是共用一块地址的。
因此我们可以画出un的内存分布图:
3、结构体和联合体的对⽐
我们再对⽐⼀下相同成员的结构体和联合体的内存布局情况。
结构体:
struct S
{char c;int i;
};struct S s = { 0 };
联合体:
union Un
{char c;int i;
};union Un un = { 0 };
画图演示:
4、联合体⼤⼩的计算
①:联合体的⼤⼩⾄少是最⼤成员的⼤⼩。
②:最⼤成员⼤⼩是最⼤对⻬数的整数倍。
例如:
#include<stdio.h>union Un1
{char c[5];//5 对齐数:1int i; //4 4
};union Un2
{short c[7];//14 对齐数:2int i; //4 4
};int main()
{printf("%zd\n", sizeof(union Un1));//8printf("%zd\n", sizeof(union Un2));//16return 0;
}
运行结果:
那么我们使用联合体有什么作用呢?其实使⽤联合体是可以节省空间的。
⽐如:我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯⼦、衬衫。每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。
如下;
上述结构设计时,我们可以把公共属性单独写出来,剩余属于各种商品本⾝的属性就使⽤联合体,这样就可以节省一部分内存空间。
如下:
#include<stdio.h>struct gift_list
{int stork_number;//库存量double author[20];//作者int num_pages;//页数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;
};int main()
{struct gift_list gl;gl.item.shirt.colors;//举例return 0;
}
5、练习
写⼀个程序,判断当前机器是⼤端还是⼩端?
#include<stdio.h>int check_sys()
{int n = 1;return *(char*)&n;//取出内存中低位字节
}
int main()
{int ret = check_sys();if (ret == 1)printf("小端\n");elseprintf("大端\n");return 0;
}
运行结果:
二、枚举
1、枚举类型的声明
枚举顾名思义就是⼀⼀列举,把可能的取值⼀⼀列举。
⽐如我们现实⽣活中:
⼀周的星期⼀到星期⽇是有限的7天,可以⼀⼀列举。
性别有:男、⼥、保密,也可以⼀⼀列举。
⽉份有12个⽉,也可以⼀⼀列举。
三原⾊,也是可以一一列举。
那么这些数据的表⽰就可以使⽤枚举:
enum Day//星期
{Mon,Tues,Wed,Thur,Fri,Sat,Sun
};enum Sex//性别
{MALE,FEMALE,SECRET
};enum Color//颜色
{RED,//0GREEN,//1BLUE//2
};
以上定义的 enum Day , enum Sex , enum Color 都是枚举类型。
{ }中的内容是枚举类型的可能取值,也叫做枚举常量 。
这些枚举常量都是有值的,默认从0开始,依次递增。
#include<stdio.h>enum Color
{RED,GREEN,BLUE
};int main()
{printf("%d\n", RED);printf("%d\n", GREEN);printf("%d\n", BLUE);return 0;
}
运行结果:
同样在声明枚举类型的时候也可以赋初值。
#include<stdio.h>enum Color
{RED = 2,GREEN = 4,BLUE = 8
};int main()
{printf("%d\n", RED);printf("%d\n", GREEN);printf("%d\n", BLUE);return 0;
}
运行结果:
2、枚举类型的使用
那么枚举类型可以应用在哪里呢?我们可以用在计算器上,让枚举常量来代替数字,让代码更具有可读性。
例如:
#include<stdio.h>int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;
}int Div(int x, int y)
{return x / y;
}void calc(int(*pf)(int, int))
{int x, y, ret;printf("请输入两个操作数:\n");scanf("%d %d", &x, &y);ret = pf(x, y);printf("结果为%d\n", ret);
}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,//0ADD,//1SUB,//2MUL,//3DIV//4
};int main()
{int input = 0;do{menu();printf("请选择: >");scanf("%d", &input);switch (input){case ADD:calc(Add);break;case SUB:calc(Sub);break;case MUL:calc(Mul);break;case DIV:calc(Div);break;case EXIT:printf("退出计算器...\n");break;default:printf("选择错误,请重新选择:\n");break;}} while (input != EXIT);return 0;
}
3、枚举类型的优点
我们可以使⽤ #define 来定义常量,那么为什么⾮要使⽤枚举?
枚举的优点:
①:增加代码的可读性和可维护性。
②:和#define定义的标识符⽐较,枚举有类型检查,更加严谨。
③:便于调试,预处理阶段会删除 #define 定义的符号(将符号直接变为常量)。
④:使⽤⽅便,⼀次可以定义多个常量。
⑤:枚举常量是遵循作⽤域规则的,枚举声明在函数内,只能在函数内使⽤。