联合体和枚举
前言
C语言中,联合体(union)是一种特殊的数据类型,允许存储不同类型的数据在同一块内存空间中。联合体的大小取决于其中最大的成员的大小,共享同一块内存空间的成员可以存储不同类型的数据。
联合体的定义方式与结构体类似,使用关键字union
,后跟联合体的名称。联合体的成员可以是任意类型的变量,包括基本数据类型、数组、指针等。
使用联合体时,可以使用成员访问运算符".
“或指针运算符”->
"来访问联合体的成员。
下面是一个联合体的示例:
#include <stdio.h> union Data { int i; float f; char str[20]; }; int main() { union Data data; data.i = 10; printf("data.i : %d\n", data.i); data.f = 3.14; printf("data.f : %f\n", data.f); strcpy(data.str, "Hello"); printf("data.str : %s\n", data.str); return 0; }
输出结果为:
data.i : 10 data.f : 3.140000 data.str : Hello
在这个示例中,联合体Data
定义了三个成员:整型变量i
、浮点型变量f
和字符数组str
。在主函数中,我们可以修改和访问这些成员,注意到修改一个成员会影响到其他成员的值。这是因为它们都共享同一个内存空间。
使用联合体时要注意成员的赋值和访问,确保类型和内存空间的正确使用。联合体主要用于在不同类型的数据之间进行转换或共享内存空间的情况。
C语言中,枚举(enum)是一种特殊的数据类型,用于定义一组具有相互关联的常量。枚举常量可以使用标识符来表示,并且可以有一个或多个枚举器(enumerator)。
枚举的定义方式如下:
enum 枚举名称 { 枚举器1, 枚举器2, ... 枚举器n };
枚举常量可以是整数常量、字符常量或字符串常量。
下面是一个使用枚举的示例:
#include <stdio.h> enum Weekday { Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday }; int main() { enum Weekday today; today = Wednesday; if (today == Wednesday) { printf("Today is Wednesday.\n"); } else { printf("Today is not Wednesday.\n"); } return 0; }
在这个示例中,我们定义了一个枚举Weekday
,包含了一周中的每天。在主函数中,我们定义了一个变量today
,然后将其赋值为Wednesday
。然后使用if
语句判断today
的值,输出相应的结果。
输出结果为:
Today is Wednesday.
枚举常量在定义时会默认从0开始递增。在上面的示例中,Monday
的值为0,Tuesday
的值为1,以此类推。
你也可以显式地为枚举常量赋值,如下所示:
enum Weekday { Monday = 1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday };
在这个示例中,Monday
的值为1,Tuesday
的值为2,以此类推。使用显式赋值可以更准确地控制枚举常量的值。
枚举在编程中常用于定义一组相关的常量,提高程序的可读性和维护性。
一、 联合体
1.1 联合体类型的声明
像结构体一样,联合体也是由一个或者多个成员构成,这些成员可以不同的类型。但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同一块内存空间。
所以联合体也叫:共用体。
给联合体其中一个成员赋值,其他成员的值也跟着变化。
#include <stdio.h> //联合类型的声明 union Un { char c; int i; }; int main() { //联合变量的定义 union Un un = {0}; //计算连个变量的大小 printf("%d\n", sizeof(un)); return 0; }
为什么是4呢?
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; }
输出结果:
001AF85C 001AF85C 001AF85C
//代码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; }
输出结果:
11223355
代码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是大端 }
除了判断大小端之外,我们还可以使用联合体来解决实际生活中碰到的取几位地址的问题
#include<stdio.h> typedef union Un { int n; struct s { char c1; char c2; char c3; char c4; }s; }Un; int main() { Un test = { 0 }; test.n = 0x11223344; printf("%x %x %x %x",test.s.c1,test.s.c2,test.s.c3,test.s.c4); return 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 };
2.2 枚举类型的优点
为什么使用枚举?
我们可以使用#define
定义常量,为什么非要使用枚举?
枚举的优点:
- 增加代码的可读性和可维护性
- 和
#define
定义的标识符比较枚举有类型检查,更加严谨。 - 便于调试,预处理阶段会删除
#define
定义的符号 - 使用方便,一次可以定义多个常量
- 枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用
2.3 枚举类型的使用
enum Color//颜色 { RED=1, GREEN=2, BLUE=4 }; enum Color clr = GREEN; //使用枚举常量给枚举变量赋值
那是否可以拿整数给枚举变量赋值呢?
在C语言中是可以的,但是在C++是不行的C++的类型检查比较严格。