自定义类型:联合与枚举
联合体
联合体类型的声明
联合体和结构体一样,都是由一个或是多个成员组成,也可以放置不同类型,关键字为union
union un
{char i;int c;
};
不过和结构体不同的是,联合体只会为最大的结构体成员分配足够的内存空间,联合体的特点是所有的成员都共用一个空间,所以又叫共用体
union un
{char i;int c;
};
int main()
{union un u = { 0 };printf("%zd\n", sizeof(union un));return 0;
}
我们来看看这个联合体长度是多少
四个位元,这正好验证了上述的话,那么只有4个空间,那么 i 去哪里了?我放到下面说
我们先将它们的地址打印出来
union un
{char i;int c;
};
int main()
{union un u = { 0 };printf("%zd\n", sizeof(union un));printf("%p\n", &u);printf("%p\n", &(u.i));printf("%p\n", &(u.c));return 0;
}
运行结果:
我们发现三个所在地址一样,我们画个图
当存储c是i会被覆盖,所以i和c是共用一个空间的
联合体的特点
联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小,再补充一点:在联合内如果有成员被改变,那么其他成员也会被改变
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;
}
这里原本存储的是 44 33 22 11
当我再走下去时变成了 55 33 22 11
因此得知当一个成员被改变时其他的成员也会变动
相同成员的结构体和联合体对比
我们来对比结构体和联合体的区别在哪?
我们先在两个不同的地方定义两个同样的成员
结构体:
struct S
{char a;int b;
};
联合体:
union un
{char a;int b;
};
我们先看输出分别是多少:
分别为8和4,再看它在的成员偏移量
结构体:
联合体:
将成员偏移量的图画出来
我们看到结构体有可能会造成空间浪费,而联合体可以节省空间
联合体大小的计算
联合的大小至少是最大成员的大小
当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍
我们先将代码写出来
union Un1
{char c[5];int i;
};union Un2
{short c[7];int i;
};int main()
{printf("%zd\n", sizeof(union Un1));printf("%zd\n", sizeof(union Un2));return 0;
}
我们先计算所需要的空间
所以输出为8和16,我们打印看看
我们发现联合体和结构体一样都会有对齐现象
在结构体和联合体的对比中我们知道联合体可以节省空间,假如我需要上线一个"礼品兑换清单",礼品中有三样商品:书籍 衬衫 杯子,每个商品都会有价格 库存量和商品类型,当然还有其他的的特殊类型
我们先直接写出一个商品清单的结构体
struct li_wu_qing_dan
{int ku_cun_liang;double jia_ge;char shang_pin_lei_xing[50];char shu_ming[20];char zuo_zhe[20];unsigned int ye_shu;char she_ji[20];int yan_se;int chi_cun;
};
我们当然也可以直接使用这个结构体,可是这样会造成内存空间不必要的浪费,那该怎么做呢?
我们先将这三样商品的共同属性提取出来,在使用联合体将商品个别的属性储存起来,这样可以节省空间
struct li_wu_qing_dan
{int ku_cun_liang;double jia_ge;char shang_pin_lei_xing[50];union{struct {char shu_ming[20];char chu_ban_she[20];int ye_shu;}book;struct {char she_ji[20];char yan_se;int chi_cun;}cup;struct {char yan_se;}shirt;}item;
};
联合的一个练习
写一个程序,判断当前机器是大端?还是小端?
union un
{char c;int i;
}un;
int main()
{union un un;un.i = 1;if (un.i == 1)printf("小端");elseprintf("大端");
}
我们先调试看看
我们看到是倒着存的,我们前面在数据在内存中的存储方式提到过大端和小端的存储方式
小端的存储模式为低位数在内存的高位地址处,高位字节在内存的高地址处,大端则是低位数在高位的地址处,高位数在地址的低位地址处,我们调试的截图位小端存储模式,所以输出为小端
我们也可以将它写成一个函数再进行判断
int chick_sys()
{union un{char c;int i;}un;union un un;un.i = 1;if (un.i == 1)return 1;elsereturn 0;
}
int main()
{if ((chick_sys) == 1)printf("小端");elseprintf("大端");
}
甚至可以直接返回un.i
int chick_sys()
{union un{char c;int i;}un;union un un;un.i = 1;return un.i;
}
int main()
{if ((chick_sys) == 1)printf("小端");elseprintf("大端");
}
枚举类型
枚举类型的声明
枚举就是将可能的取值一一列举出来
例如:一周有七天
性别有 男 女 和保密
三原色有 红 黄 蓝
这些都是可以列举的
enum Day
{Mon,Tues,Wed,Thur,Fri,Sat,Sun
};enum Sex
{MALE,FEMALE,SECRET
};enum Color
{RED,GREEN,BLUE
};
上述的 enum Day ,enum Sex, enum Color都是枚举类型,枚举的结构成员是可能取值,叫做枚举常量,这些可能取值都是有值,默认从0开始,我们将它打印出来
enum Color
{RED,GREEN,BLUE
};int main()
{printf("%d\n", RED);printf("%d\n", GREEN);printf("%d\n", BLUE);return 0;
}
也可以在声明枚举类型是进行赋值
enum Color
{RED = 2,GREEN = 4,BLUE = 8
};int main()
{printf("%d\n", RED);printf("%d\n", GREEN);printf("%d\n", BLUE);return 0;
}
枚举类型的优点
为什么要使用枚举?
不是#define也可以定义常量吗,为什么非要用枚举呢?\
下列是优点:
1.增加代码的可读性
2.和#define相比枚举有类型检查,更加严谨
3.方便调试,预处理阶段会删除#define定义的符号
4.使用方便,可以定义多个常量
5.枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用
枚举类型的使用
前面也说了如何使用枚举,先创建枚举常量再给枚举变量赋值
enum Color
{RED = 2,GREEN = 4,BLUE = 8
};
int main()
{enum Color c = 4;return 0;
}
那么是否可以拿整数给变量赋值呢,C语言可以
但是在C++中会报错