一 ,联合体类型的声明
与结构体相似,联合体也是由一个或者多个成员构成,这些成员可以是不同类型。
但是与结构体不同的是 : 编译器只为联合体成员中的最大成员分配足够的内存空间。 联合体的特点是所有成员共用一块内存空间。所以联合体 也称 ===> 共用体
那也就意味着联合体其中一个成员赋值,其他成员的值也跟着变化。
union tag
{
member-list;
}variable - list ;
//联合体类型声明
union U
{char c;int n;
};
int main()
{//联合体类型定义union U un = { 0 };//计算联合体变量的大小printf("%zd\n", sizeof(un));//4return 0;
}
输出结果 : 4
并不是 5 也不是 8 ;这就与联合体成员在内存中的分布有关;
二 ,联合体的特点
2.1 联合体的内存布局
联合体的成员是共用一块内存空间的,这个联合变量的大小,至少是最大成员的大小(因为至少得有能力保存最大的那个成员)
union U
{char c;int n;
};
int main()
{union U u = { 0 };printf("联合体大小 : %zd\n", sizeof(u));printf("%p\n", &u);printf("%p\n", &u.c);printf("%p\n", &u.n);return 0;
}
那就意味这在这块联合体的内存空间中,变量分布是:
初始化数值,进入调试,查看内存情况验证:
经过分析可以画出 , u 的内存布局图
2.2 联合体与结构体的对比
struct S
{char c;int n;
};union U
{char c;int n;
};
int main()
{printf("S : %zd\n", sizeof(struct S));printf("U : %zd\n", sizeof(union U));return 0;
}
联合体与结构体的内存图:
三 ,联合体大小的计算
- 联合的大小至少是最大成员的大小。
- 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
例题一:
union u
{char c[5];int n;
}un1;
int main()
{printf("%zd\n", sizeof(un1));return 0;
}
例题二:
union u
{short c[7];int i;
}n1;
int main()
{printf("%zd\n", sizeof(n1));return 0;
}
使用联合体是可以节省空间的。
比方说,有个活动,可以进行礼品兑换,可进行兑换中有三种商品:图书 ,杯子 ,衬衫。
每一种商品都有:库存量 ,价格 ,商品类型 和 商品类型相关的其他信息
- 图书 : 书名 ,作者 ,页数
- 杯子 : 设计
- 衬衫 : 设计 ,可选颜色 ,可选尺寸
进而就会考虑到使用结构体 把数据统统放进去,可这样会对空间造成一定的浪费。结构中包含了所有的礼品的各种属性,但是只有部分属性信息是常用的,
比方说:商品是图书,就不需要 design , colors , sizies 。
struct gift_list
{//公共属性int stock_number;//库存量double price;//定价int item_type;//商品类型//特殊属性char title[20];//书名char author;//作者int num_pages;//页数char design[30];//设计int color;//颜色int sizes;//尺寸
};
所以可以把公共属性单独写出来,剩余属于各种商品本身的属性使用联合起来,这样就可以在一定程度上节省了空间。
struct gift_list
{//公共属性int stock_number;//库存量double price;//定价int item_type;//商品类型union{struct {char title[20];//书名char author;//作者int num_pages;//页数}book;struct{char design[30];//设计}mug;struct{char design[30];//设计int color;//颜色int sizes;//尺寸}shirt;}item;
};
需要用到谁,就可以开辟谁的空间。可以使用匿名结构体 和 匿名联合体 ,因为只需要用到成员就好了,不需要用到类型。
2.3 练习
写一个程序,判断当前机器是小端还是大端?
//法一
int main()
{int a = 1;//0x00 00 00 01if (*(char*)&a == 1)printf("小端\n");elseprintf("大端\n");return 0;
}//法二
int check_sys()
{int a = 1;if (*(char*)&a == 1)return 1;elsereturn 0;
}
int main()
{int ret = check_sys();if (ret == 1)printf("小端\n");elseprintf("大端\n");return 0;
}//法三 -- 联合体
int check_sys()
{union{char c;int n;}n;n.n = 1;return n.c;
}
int main()
{int ret = check_sys();if (ret == 1)printf("小端\n");elseprintf("大端\n");return 0;
}
四 ,枚举类型的声明
枚举顾名思义就是 一 一 列举 (列举可能 有限 的值)
比如:
- 一周中周一到周日 有限7天
- 性别:男 , 女 ,保密
- 月份有12个月
- 三原色:红绿蓝
这些数据的表示就可以使用枚举
举例:
enum Day//星期
{Mon,Tues,wed,Thur,Fri,Sat,sun
};enum Sex//性别
{MALE,FEMALE,SACRET
};enum Color//颜色
{RED,GREEN,BULE
};
书写格式:
- 定义的 enum Day ,enum Sex , enum Color 都是枚举类型
- { } 中的内容是枚举类型的可能值 , 也叫枚举常量
- 这些枚举常量都是有值的 , 默认从0开始 , 依次递增 1 ,枚举常量是不可以被修改的,但是可以赋初始值。
1.三个都可以各自赋初始值
2. 仅对第一个常量赋值 , 其他常量依次增1
3.对中间的常量赋值 , 第一个常量会默认为0 , 往后依次增加 1 到 赋值的中间常量 , 中间常量往后 , 以中间常量的值为起始,依次增加 1 ;
五 ,枚举类型的优点
为什么要使用枚举?
用#define 定义常量 或者说 在函数里创建一个变量 int sex = 0 // 1 2
也都可以表示男 , 女 这些值
枚举的优点
- 增强代码的可读性和可维护性
- 和#define 定义的标识符比较 枚举有类型检查,更加严谨
- 便于调试,预处理阶段会删除 #define 定义的符号
- 使用方便 , 依次可以定义多个常量
- 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用
六 ,枚举类型的使用
enum color//颜色
{red,green,bule
};int main()
{enum color crl = green;return 0;
}
使用枚举常量给枚举变量赋值
注意 : 在C语言中可以拿整数给枚举变量赋值,因为C中的类型检查不够严格 , 但是在C++中会报错