在上一篇博客中和大家分享了自定义类型的一种:结构体,这篇和大家聊聊其他的自定义类型:联合和枚举。
1. 联合体
1.1 联合体类型的声明
像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同⼀块内存空间。所以联合体也叫:共用体。下面我们看这段代码来了解一下:
#include<stdio.h>
union U//联合体声明
{
char a;
int b;
};
int main()
{
union U a={0};//联合变量的定义
printf("%d\n",sizeof(a));//计算联合变量的大小
return 0;
}
为什么是4呢?这里我们就要提到联合体的特点了:联合的成员是共⽤同⼀块内存空间的,这样⼀个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。我们一起看下面的代码:
#include<stdio.h>
union U
{
char a;
int b;
};
int main()
{
union U s={0};
printf("%p\n",&(s.a));
printf("%p\n",&(s.b));
printf("%p\n",&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\n",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\n",sizeof(union U1));
printf("%d\n",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("小端\n");
}
else
{
printf("大端\n");
}
return 0;
}
既然在联合体这提到这个问题那就说明联合体也是可以写滴,请看下面的代码:
#include<stdio.h>
int main()
{
union
{
char c;
int i;
}u;
u.i=1;
if(u.c==1)
{
printf("小端\n");
}
else
{
printf("大端\n");
}
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("******************\n");
printf("*** 1.add 2.sub***\n");
printf("******************\n");
}
enum option
{
add=1,
sub=2
};
int main()
{
int input=0;
do
{
menu();
printf("请选择:\n");
scanf("%d",&input);
switch(input)
{
case add: //这里用add表示会比用1表示可读性会高
break;
case sub: //同理
break;
}
}
return 0;
}
本篇博客到这里就结束啦,大家有问题可以评论出来或者私信我哦。