在上一篇文章中,我们了解到在C语言中,自定义类型有三种,这里我们介绍后两种,联合体和枚举。
一.联合体
1.联合体的声明
像结构体一样,联合体也是由一个或多个成员构成,这些成员的类型可以是不一样的,与结构体不同的是,编译体只为联合体中最大的成员分配足够的内存空间。联合体的特点是所有成员共用同一块内存空间,所以联合体也叫:共用体。
联合体的关键字是union,我们可以举例写出一个联合体的定义与初始化
union Un
{char c;//1int i;//4
};int main()
{union Un u = {0};return 0;
}
2.联合体的特点
联合的成员是共用同一块内存空间的,他们的首地址是相同的,联合变量的带线啊哦,至少是最大的成员的大小。
现在,我们来看一下联合体的大小和地址
union Un
{char c;//1int i;//4
};int main()
{union Un u = {0};printf("%zd\n", sizeof(u));//大小为4printf("%p\n%p\n%p\n", &u,&u.i,&u.c);return 0;
}
我们会发现,大小为4,而输出地址时三者是相同的。
既然是这样的,那我们是否可以通过更改c的值,来改变i的值呢?
int main()
{union Un u = { 0 };u.i = 0x11223344;u.c = 0x55;return 0;
}
我们会发现,i的结果发生了变化,变成了0x11223355
下面,我们来对比结构体和联合体内存布局的情况。
我们发现,联合体节省了很多的空间。
因此,我们清楚了,一般来说,我们在使用某一个联合体的时候,都会使用联合体的一个变量。
3.联合体大小的计算
联合的大小至少是最大成员的大小,但是,当最大成员的大小不是最大对齐数的整数倍时候,就要对齐到最大对齐数的整数倍
我们写出如下的代码,进行分析
union Un
{char c[5];int i;
};
在这个代码中,最大成员的大小是5,但是联合体的大小并不是5,char类型的对齐数是1,int类型的对齐数是4,因此最大对齐数是4,所以,我们在设计联合体的时候,设计的大小应该是8
现在,我们再看一个代码来举例一下
union Un
{short c[7];int i;
}
在这个联合体中,大小是16,一定要注意联合体的大小啊哦是最大对齐数的整数倍
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;//尺⼨
};
但是,这样书写的结构体占据了很大的空间,且有的内容在定义时是用不到的,因此,我们就可以考虑使用联合体的方法来实现。
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;
};
5.联合体判断大小端
在之前,我们已经可以写出一个函数来判断大小端了,那么我们是否可以利用联合体的性质,来写出一个程序呢?
int check_sys()
{union{int i;char c;}un;un.i = 1;return un.c;//返回1是⼩端,返回0是⼤端
}
这样,就可以实现啦。
二.枚举
1.枚举的声明
枚举实际就是一一列举,比如,我们可以列举一周有七天,性别有男女,月份有十二个月等等。
现在,我们开始写出枚举的式子吧。
enum Sex//性别
{MALE,FEMALE,SECRET
};enum Color//颜⾊
{RED,GREEN,BLUE
};
这些都可以表示枚举。
当然,我们也可以进行创建和初始化。
int main()
{enum Color color = RED;//RED = 5;//errprintf("%d\n", RED);printf("%d\n", GREEN);printf("%d\n", BLUE);return 0;
}
在输出枚举的结果的时候,我们可以发现,如果我们不更改数据,RED代表的就是0,GREEN代表1,依次类推,我们是不能在主函数内进行更改的,这是因为,当枚举创建的时候,就是一个枚举常量,如果我们想更改数据,就必须要在枚举创建的时候赋初始值。
enum Color
{//三原色的可能取值,枚举常量RED=5,//5GREEN,//6BLUE//7
};
当然,也可以这样更改
enum Color
{//三原色的可能取值,枚举常量RED,//0GREEN=5,//5BLUE//6
};
2.枚举的优点
我们会发现,枚举的内容在#define也可以做到,那么,为什么非要使用枚举呢?
1. 增加代码的可读性和可维护性
便于书写和维护
2. 和#define定义的标识符⽐较枚举有类型检查,更加严谨。
在C++中,把枚举单独认为是一种特定的类型,因此,不规范时会报错,使得程序更加严谨。
3. 便于调试,预处理阶段会删除 #define 定义的符号
在调试中,不会删除枚举中的内容
4. 使⽤⽅便,⼀次可以定义多个常量
5. 枚举常量是遵循作⽤域规则的,枚举声明在函数内,只能在函数内使⽤