嘿嘿,家人们,今天咱们来详细剖析C语言中的联合与枚举,好啦,废话不多讲,开干!
联合体像结构体一样,联合体也是由一个或多个成员构成,这些成员可以是不同的类型.但是编译器只能为最大的成员分配足够的内存空间.联合体的特点是所有成员共用一块内存空间,所以联合体也叫:共同体.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
union Un
{
char _c;
int _i;
};
int main()
{
//联合变量的定义
union Un u1 = {'c'};
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
union Un
{
char _c;
int _i;
};
int main()
{
//联合变量的定义
union Un u1 = {'c'};
printf("%d\n", sizeof(u1));
return 0;
}在结构体那一章节,博主讲到了,结构体的大小要遵循内存对齐的规则,那么联合体要不要遵从内存对齐的规则,如果遵从的话,上面的代码计算出来的结果是8(原因博主不过多解释哈,忘了滴uu可以去去博主那篇结构体的博客).

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
union Un
{
char _c;
double _i;
};
int main()
{
//联合变量的定义
union Un u1 = {'c'};
printf("%d\n", sizeof(u1));
return 0;
}
上面的两段代码输出结果是4与8,那么为什么是4与8呢?这里就要讲到联合体的特点了
联合体的成员是共用一块内存空间的,这样一个联合体变量的大小,至少是最大成员的大小(因为联合体至少得有能力保存最大的那个成员),而上面的代码中最大成员int类型,因此结果为4
#include <stdio.h>
int main()
union Un
{
char _c;
int _i;
};
{
union Un u1 = {0};
printf("&u1._c == %p\n",&u1._c);
printf("&u1._i == %p\n",&u1._i);
printf("&u1 == %p\n",&u1);
return 0;
}
上面的代码输出的三个均是一模一样,那么我们仔细便可以画图u1的内存分布图.

#include <stdio.h>
int main()
union Un
{
char _c;
int _i;
};
{
union Un u1 = { 0 };
u1._i = 0x11223344;
u1._c = 0x55;
printf("%x\n", u1._i);
return 0;
}

通过上面的代码我们可以分析出,将变量i的第4个字节改成了55,那么因此我们可以画出其对应的内存分布图.

我们再来对比一下,相同成员的结构体与联合体的内存布局情况.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
struct S
{
char _c;
int _i;
};
int main()
{
struct S s1 ={'c'};
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
union Un
{
char _c;
int _i;
};
int main()
{
union Un u = {0};
return 0;
}

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
union Un1
{
char c[5];
int _i;
};
int main()
{
printf("Un1的大小为:> %zd\n", sizeof(union Un1));
return 0;
}

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
union Un2
{
short s[5];
int _i;
};
int main()
{
printf("Un2的大小为:> %zd\n", sizeof(union Un2));
return 0;
}

使用联合体是可以节省空间的,例如: 我们要搞一个活动,要上线一个礼品兑换单,礼品兑换单中有三种商品: 图书、杯子、衬衫 . 每一种商品都有: 库存量、价格、商品类型和商品类型相关的其他信息.
如果我们使用结构体来描述的话,将会是如下代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
struct Gift_List
{
//库存量
int _Inventory;
//价格
double _Price;
//商品类型
int Type_Of_Merchandise;
//书的特殊属性
char _Name[20];
//书的作者
char _Author[20];
//书的页数
int _Pages;
//衬衫的颜色
char _Color[20];
//衬衫的尺寸
int _Sizes;
//三者的设计
char _Desigin[20];
//三者的品牌
char _Brand[20];
};
int main()
{
printf("%zd\n", sizeof(struct Gift_List));
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
struct Gift_List
{
//库存量
int _Inventory;
//价格
double _Price;
//商品类型
int Type_Of_Merchandise;
//三者的设计
char _Desigin[20];
//三者的品牌
char _Brand[20];
union
{
struct Book
{
//书的特殊属性
char _Name[20];
//书的作者
char _Author[20];
//书的页数
int _Pages;
};
struct Shirt
{
//衬衫的颜色
char _Color[20];
//衬衫的尺寸
int _Sizes;
};
};
};
int main()
{
printf("Gift_List(嵌套联合体):>%zd\n", sizeof(struct Gift_List));
return 0;
}

通过对比二者,我们可以清晰地发现,使用联合体可以大大地减少内存空间的占用.
在数据类型在内存的存储方式那篇博客中,我们讲到了大小端的概念.
在那里我们通过拿到数值1的最低位并且按位与1来判断来判断程序的大小端
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int check_sys()
{
int value = 1;
return (*(char*)&value & 1);
}
int main()
{
int result = check_sys();
if(result == 0)
{
printf("大端存储\n");
}
else
{
printf("小端存储\n");
}
}

除了上面的方式,我们也可以通过联合体的方式来进行求解,联合体的特点是
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int Check_sys()
{
union
{
int _i;
char _c;
}un;
un._i = 1;
return un._c;
}
int main()
{
int result = Check_sys();
if (result == 0)
{
printf("大端存储\n");
}
else
{
printf("小端存储\n");
}
}

枚举顾名思义就是⼀⼀列举, 把可能的取值⼀⼀列举, 比如我们现实生活中:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
//星期
enum Day
{
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
//性别
enum Sex
{
Boy,
Girl,
Other
};
//颜色
enum Color
{
Red,
Green,
Blue
};
int main()
{
printf("Day:Monday> %d\n", Monday);
printf("Sex:Girl> %d\n", Girl);
printf("Color:Blue> %d\n", Blue);
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
//星期
enum Day
{
Monday = 1,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
//性别
enum Sex
{
Boy = 5,
Girl,
Other
};
//颜色
enum Color
{
Red,
Green,
Blue = 2
};
int main()
{
printf("Day:Monday> %d\n", Monday);
printf("Sex:Girl> %d\n", Girl);
printf("Color:Blue> %d\n", Blue);
return 0;
}
我们可以使用#define定义常量,但是为什么非要使用枚举呢
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
//性别
enum Sex
{
Boy = 5,
Girl,
Other
};
int main()
{
enum Sex Tom = Boy;
printf("%d\n", Tom);
return 0;
}
那是否可以拿整数给枚举变量赋值呢?在C语言中是可以的,但是在C++是不行的,C++的类型检查比较较严格。
也许大部分uu没有听说柔性数组的概念,但是她确实是存在滴,在C99中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
typedef struct Flexible_Array
{
int _i;
//柔性数组成员,最后一个元素时未知大小的数组,在[]中填0表示数组的大小是未知的
int a[0];
}type_a;对于代码1,有些编译器可能会报错导致无法编译,因此将a[0]中的0去掉.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
typedef struct Flexible_Array
{
int _i;
//柔性数组成员,最后一个元素时未知大小的数组
int a[];
}type_a;#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
typedef struct Flexible_Array
{
double _i;
//柔性数组成员,最后一个元素时未知大小的数组,在[]中填0表示数组的大小是未知的
int a[];
}type_a;
int main()
{
printf("%zd\n",sizeof(type_a));
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
typedef struct Flexible_Array
{
int _i;
//柔性数组成员,最后一个元素时未知大小的数组,在[]中填0表示数组的大小是未知的
int a[];
}type_a;
int main()
{
int i = 0;
type_a* p = (type_a*)malloc(sizeof(type_a) + 100 * sizeof(int));
for (i = 0; i < 100; i++)
{
p->a[i] = i;
}
free(p);
return 0;
}柔性数组a[]虽然在编译的时候没有固定大小,但是在运行时通过malloc分配了额外的内存,这部分内存与结构体的其他成员是在一起连续分配的,由于malloc分配了这部分内存,并且在结构体中按需访问这些元素是安全的因此柔性数组成员a[]能够通过动态分配获得 100 个整型元素的连续空间.

上述的type_a结构也可以设计为下面的结构,也能完成同样的效果.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
typedef struct Flexible_Array
{
int _i;
int * pa;
}type_a;
int main()
{
int i = 0;
type_a * p = (type_a *)malloc(sizeof(type_a) + 100 * sizeof(int));
p->_i = 100;
p->pa = (int *)malloc(sizeof(int) * p->_i);
//进行赋值
for(i=0; i<100; i++)
{
p->pa[i] = i;
}
//先释放内部
free(p->pa);
p->pa = NULL;
//再释放外部
free(p);
p = NULL;
return 0;
}

好啦,uu们,C语言的联合与枚举这部分滴详细内容博主就讲到这里啦,如果uu们觉得博主讲的不错的话,请动动你们滴小手给博主点点赞,你们滴鼓励将成为博主源源不断滴动力,同时也欢迎大家来指正博主滴错误.