在上一篇博客中和大家分享了自定义类型的一种:结构体,这篇和大家聊聊其他的自定义类型:联合和枚举。
1. 联合体
1.1 联合体类型的声明
像结构体?样,联合体也是由?个或者多个成员构成,这些成员可以不同的类型。但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同?块内存空间。所以联合体也叫:共用体。下面我们看这段代码来了解一下:
#include<stdio.h> union U//联合体声明 { char a; int b; }; int main() { union U a={0};//联合变量的定义 printf("%d ",sizeof(a));//计算联合变量的大小 return 0; }
为什么是4呢?这里我们就要提到联合体的特点了:联合的成员是共?同?块内存空间的,这样?个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。我们一起看下面的代码:
#include<stdio.h> union U { char a; int b; }; int main() { union U s={0}; printf("%p ",&(s.a)); printf("%p ",&(s.b)); printf("%p ",&s); return 0; }
通过上面的代码我们可以知道s的地址为012FFCF4,当我们要取b的地址时发现它也是从这里开始的,b占4个字节即蓝色部分,a也是从这里开始,它占1个字节,即橙色部分。我们发现使用b的时候它4个字节都使用,使用a的时候只使用橙色部分,橙色部分是它们共用的空间。 给联合体其中?个成员赋值,其他成员的值也跟着变化。我们看下面一段代码:
#include<stdio.h> union U { char a; int b; }; int main() { union U c={0}; c.b=0x11223344; c.a=0x55; printf("%x ",c.b); return 0; }
1.2 相同成员的联合体和结构体对比
我们对比一下相同成员的联合体和结构体的内存布局情况:
struct S { char a; int b; }; struct S a={0}; union U { char a; int b; }; union U a={0};
在结构体中,a和b是分开来储存的,在联合体中,a和b共用一块共同的空间,这是他们的区别。
1.3 联合体的大小计算
联合的大小至少是最大成员的大小。当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。那我们一起看下面的代码进行了解:
#include<stdio.h> union U1 { char a[6]; int b; }; union U2 { short a[7]; int b; }; int main() { printf("%d ",sizeof(union U1)); printf("%d ",sizeof(union U2)); return 0; }
这里大家先思考一下U1和U2的内存大小是多少?接着我们调试一下这段代码看一下答案:
这里大家是否在疑惑为什么不是6和14呢? char a[6]占6个字节能放下int b啊?别急听我慢慢给大家分析:对于char a[6]的对齐数是怎么算的,它是char类型的数组,它的对齐数是按照它每个成员来算,它每个成员自身大小是1个字节,所以它的对齐数是1,而b的对齐数是4,所以在联合体U1中最大对齐数是4,又因为最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍,所以U1占8个字节,同理U2占16个字节。
1.4 联合体的使用
了解完上面的讲解之后,那联合体有什么用呢?根据联合体的特点我们可以用在a和b不同时用的情况下,比如:我们要搞?个活动,要上线?个礼品兑换单,礼品兑换单中有三种商品:图书、杯?、衬衫。每?种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。
图书:名称、作者、页数
杯子:容量
衬衫:设计、颜色、尺寸
我们用结构写一下看看:
struct gift { int stock_numbers;//库存量 double price;//定价 int item_type;//商品类型 char title[20];//书名 char author[20];//作者名字 int num_pages;//页数 int capacity;//容量 char design[30];//设计 int colors;//颜色 int sizes;//尺寸 }
上述的结构其实设计的很简单,用起来也方便,但是结构的设计中包含了所有礼品的各种属性,这样使得结构体的大小就会偏大,比较浪费内存。因为对于礼品兑换单中的商品来说,只有部分属性信息是常?的。比如:
商品如果是图书的话,那就不需要:capacity、design、colors、sizes等信息。
所以我们就可以把公共属性单独写出来,剩余属于各种商品本身的属性使用联合体起来,这样就可以介绍所需的内存空间,?定程度上节省了内存。
struct gitf { int stock_number; double price; int item_type; union { struct { char title[20]; char suthor[20]; int num_pages; }book; struct { int capacity; }mug; struct { char design[30]; int colors; int sizes; }shirt; }item; }
1.5 练习
写一个程序判断当前机器是大端还是小端,会指针的朋友可能会这样写:
#include<stdio.h> int main() { int a=1; if(*(char*)&a==1) { printf("小端 "); } else { printf("大端 "); } return 0; }
既然在联合体这提到这个问题那就说明联合体也是可以写滴,请看下面的代码:
#include<stdio.h> int main() { union { char c; int i; }u; u.i=1; if(u.c==1) { printf("小端 "); } else { printf("大端 "); } return 0; }
按照联合体的设计,第一个字节的位置c和i共用,给u.i赋值1,它用4个字节,u.c拿到的是它的第一个字节的位置,所以我们可以通过判断u.c的值来判断大小端。
2. 枚举
2.1 枚举类型的声明
枚举顾名思义就是一一列举,把可能的取值一一列出来。比如:
一周从星期一到星期日是有限的7天,可一一列举出来。
一年有12个月份也可以一一列举出来。
就拿一周来举例,这些数据的表示就可以使用枚举了:
enum day { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday };
上面定义的enum day就是枚举类型,{}内的内容是枚举类型的可能取值,也叫枚举常量。这些可能取值都是有值的,默认从0开始,依次递增1,当然在声明枚举类型的时候也可以赋初值。
enum color { red=2, green=7, blue=5 };
2.2 枚举类型的优点
为什么使用枚举呢?我们也可以使用#define定义常量,为什么非要使用枚举?枚举的优点:
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 便于调试,预处理阶段会删除 #define 定义的符号
4. 使用方便,?次可以定义多个常量
5. 枚举常量是遵循作?域规则的,枚举声明在函数内,只能在函数内使用
这里我们举个例子:用代码实现计算器(因为之前写了好多次了,这里就不详细写了,只是想用这段代码让大家了解枚举的优点)
#include<stdio.h> void menu() { printf("****************** "); printf("*** 1.add 2.sub*** "); printf("****************** "); } enum option { add=1, sub=2 }; int main() { int input=0; do { menu(); printf("请选择: "); scanf("%d",&input); switch(input) { case add: //这里用add表示会比用1表示可读性会高 break; case sub: //同理 break; } } return 0; }
本篇博客到这里就结束啦,大家有问题可以评论出来或者私信我哦。