目录
一、联合体
1.1 联合体类型的声明
1.2 联合体的特点
1.3 相同成员的结构体和联合体对比
1.4 联合体大小的计算
1.5 联合的⼀个练习
二、枚举类型
2.1 枚举类型的声明
2.2 枚举类型的优点
2.3 枚举类型的使用
一、联合体
1.1 联合体类型的声明
像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。
但是编译器只为最⼤的成员分配足够的内存空间。联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共用体。
给联合体其中⼀个成员赋值,其他成员的值也跟着变化。
创建联合和创建结构的方式相同,需要一个联合模板和联合变量。下面是几种定义联合体变量的方法:
方法一:先创建模板,再定义变量
// 创建联合体模板union perdata union perdata {int Class;char Office; }; // 使用该联合体模板创建两个变量a, b union perdata a,b;
此处,
perdata
是联合体名,该名字是由我们任意定的,但是尽量起个有意义的名称。其相当于一个模板,可以使用这个模板去定义变量a、b
。定义的时候不要忘了union
。方法二:同时创建模板和变量
// 创建联合体模板union perdata的同时定义两个变量a、b union perdata {int Class;char Office; }a,b;
这与方法一差不多。
方法三:省略联合体名
union {int Class;char Office; }a,b;
相对于方法一与方法二,此处省略了联合体名。虽然更简洁了,但是因为没有了名字,后面就不能用该联合体定义新的变量。
方法四:使用typedef
// 联合体模板union perdata重新命名为perdata_U typedef union perdata {int Class;char Office; }perdata_U; // 使用新名字perdata_U创建两个变量a, b perdata_U a,b;
此处使用
typedef
为联合体模板union perdata
定义一个别名perdata_U
。初始化联合体
联合体的初始化与结构体不同,联合体只能存储一个值。联合体有三种初始化方法:
perdata_U a; a.Class = 10; perdata_U b = a; /* 1、把一个联合初始化为另一个同类型的联合; */ perdata_U c = {20}; /* 2、初始化联合的第一个成员; */ perdata_U d = {.Office = 30}; /* 3、根据C99标准,使用指定初始化器。 */
1.2 联合体的特点
联合的成员是共用同⼀块内存空间的,这样⼀个联合变量的大小,至少是最大成员的大小(因为联合至少得有能力保存最大的那个成员)。
//代码1
#include <stdio.h>
//联合类型的声明
union Un
{char c;int i;
};
int main()
{//联合变量的定义union Un un = { 0 };// 下⾯输出的结果是⼀样的吗?printf("%p\n", &(un.i));printf("%p\n", &(un.c));printf("%p\n", &un);return 0;
}
输出的结果:
//代码2
#include <stdio.h>
//联合类型的声明
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;
}
输出的结果:
代码1输出的三个地址⼀模⼀样,代码2的输出,我们发现将i的第4个字节的内容修改为55了。 我们仔细分析就可以画出,un的内存布局图。
1.3 相同成员的结构体和联合体对比
我们再对比⼀下相同成员的结构体和联合体的内存布局情况。
struct S
{char c;int i;
};
struct S s = {0};union Un
{char c;int i;
};
union Un un = {0};
1.4 联合体大小的计算
• 联合的大小至少是最大成员的大小。
• 当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍。
#include <stdio.h>
union Un1
{char c[5];int i;
};
union Un2
{short c[7];int i;
};
int main()
{//下⾯输出的结果是什么?printf("%d\n", sizeof(union Un1));printf("%d\n", sizeof(union Un2));return 0;
}
使用联合体是可以节省空间的,举例:
比如,我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯子、衬衫。 每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。
图书:书名、作者、页数
杯子:设计
衬衫:设计、可选颜⾊、可选尺寸
那我们不耐⼼思考,直接写出⼀下结构:
struct gift_list
{//公共属性int stock_number;//库存量double price; //定价int item_type;//商品类型//特殊属性char title[20];//书名char author[20];//作者int num_pages;//⻚数char design[30];//设计int colors;//颜⾊int sizes;//尺⼨
};
上述的结构其实设计的很简单,⽤起来也方便,但是结构的设计中包含了所有礼品的各种属性,这样使得结构体的大小就会偏大,比较浪费内存。因为对于礼品兑换单中的商品来说,只有部分属性信息是常⽤的。比如:
商品是图书,就不需要design、colors、sizes。
所以我们就可以把公共属性单独写出来,剩余属于各种商品本⾝的属性使用联合体起来,这样就可以介绍所需的内存空间,⼀定程度上节省了内存。
struct gift_list
{int stock_number;//库存量double price; //定价int item_type;//商品类型union {struct{char title[20];//书名char author[20];//作者int num_pages;//⻚数}book;struct{char design[30];//设计}mug;struct{char design[30];//设计int colors;//颜⾊int sizes;//尺⼨}shirt;}item;
};
1.5 联合的⼀个练习
写⼀个程序,判断当前机器是大端?还是小端?
int check_sys()
{union{int i;char c;}un;un.i = 1;return un.c;//返回1是⼩端,返回0是⼤端
}
二、枚举类型
2.1 枚举类型的声明
枚举顾名思义就是⼀⼀列举。
把可能的取值⼀⼀列举。
比如我们现实生活中:
⼀周的星期⼀到星期日是有限的7天,可以一一列举
性别有:男、女、保密,也可以一一列举
月份有12个月,也可以⼀⼀列举
三原色,也是可以意义列举
这些数据的表示就可以使用枚举了。
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开始,依次递增1,当然在声明枚举类型的时候也可以赋初值。
enum Color//颜⾊
{RED=2,GREEN=4,BLUE=8
};
枚举类型需要先定义后使用,这里的定义是类型的定义,不是枚举变量的定义。
既然枚举也是一种数据类型,那么它和基本数据类型一样也可以对变量进行声明。
方法一:枚举类型的定义和变量的声明分开
//先定义类型 enum DAY //类型名称就是enum DAY {MON=1, TUE, WED, THU, FRI, SAT, SUN };//后声明变量 enum DAY yesterday; enum DAY today; enum DAY tomorrow; //变量tomorrow的类型为枚举型enum DAY enum DAY good_day, bad_day; //变量good_day和bad_day的类型均为枚举型enum DAY
方法二:类型定义与变量声明同时进行
enum //跟第一个定义不同的是,此处的标号DAY省略,这是允许的。 {saturday,sunday = 0,monday,tuesday,wednesday,thursday,friday } workday; //变量workday的类型为枚举型enum DAY//变量days的类型为枚举型enum week enum week { Mon=1, Tue, Wed, Thu, Fri Sat, Sun} days; //定义枚举类型并声明了两个枚举型变量 enum BOOLEAN { false, true } end_flag, match_flag;
第二种方式的变量定义是一次性的,后面要想再定义同类型变量就不行了。
第一种方式更灵活。
方法三:用typedef关键字将枚举类型定义成别名,并利用该别名进行变量声明
typedef enum workday {saturday,sunday = 0,monday,tuesday,wednesday,thursday,friday } workday; //此处的workday为枚举型enum workday的别名//或者 //enum workday中的workday可以省略 typedef enum {saturday,sunday = 0,monday,tuesday,wednesday,thursday,friday } workday; //此处的workday为枚举型enum workday的别名//定义变量 //变量today和tomorrow的类型为枚举型workday,也即enum workday workday today, tomorrow;
注意:
同一个程序中不能定义同名的枚举类型,不同的枚举类型中也不能存在同名的命名常量。
2.2 枚举类型的优点
为什么使用枚举?
我们可以使用 #define 定义常量,为什么非要使用枚举?
枚举的优点:
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 便于调试,预处理阶段会删除 #define 定义的符号
4. 使用方便,⼀次可以定义多个常量
5. 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用
2.3 枚举类型的使用
1、先声明后赋值(常用)
#include<stdio.h>/* 定义枚举类型 */ enum DAY { MON=1, TUE, WED, THU, FRI, SAT, SUN };void main() {/* 使用基本数据类型声明变量,然后对变量赋值 */int x, y, z;x = 10;y = 20;z = 30;/* 使用枚举类型声明变量,再对枚举型变量赋值 */enum DAY yesterday, today, tomorrow;yesterday = MON;today = TUE;tomorrow = WED;printf("%d %d %d \n", yesterday, today, tomorrow); }
就像各种类型都有取值范围一样,枚举变量只能接收枚举类型中定义好的符号值(实质是一个int类型的数据)。
2、声明的同时赋值
#include <stdio.h>/* 定义枚举类型 */ enum DAY { MON=1, TUE, WED, THU, FRI, SAT, SUN };void main() {/* 使用基本数据类型声明变量同时对变量赋初值 */int x=10, y=20, z=30;/* 使用枚举类型声明变量同时对枚举型变量赋初值 */enum DAY yesterday = MON, today = TUE,tomorrow = WED;printf("%d %d %d \n", yesterday, today, tomorrow); }
3、类型定义、变量声明和赋值同时进行(不推荐,会引入全局变量)
#include <stdio.h>/* 定义枚举类型,同时声明该类型的三个变量,并赋初值。它们都为全局变量 */ enum DAY {MON=1, TUE,WED,THU,FRI,SAT,SUN } yesterday = MON, today = TUE, tomorrow = WED;/* 定义三个具有基本数据类型的变量,并赋初值。它们都为全局变量 */ int x = 10, y = 20, z = 30;void main() {printf("%d %d %d \n", x, y, z); //输出:10 20 30printf("%d %d %d \n", yesterday, today, tomorrow); //输出:1 2 3 }
补充
对枚举型的变量可以直接赋任意整数值,如果赋值浮点数,也会自动去掉小数部分。
赋整数值时,可直接赋值,因为枚举的本质就是整型(int)数据的集合。
#include <stdio.h>int main() { enum week{MON, TUE, WEN};enum week oneday = 100;printf("%d,Hello, World! \n", oneday); //100,Hello, World! }
其实,更正规的做法是进行显式的强制类型转换:
#include <stdio.h>int main() { enum week{MON, TUE, WEN};enum week oneday = (enum week)100;printf("%d,Hello, World! \n", oneday); //100,Hello, World! }
但是不强制转换也可以,和第一个示例一样,不加的话就是隐式类型转换,不会出错。