C语言——联合和枚举

发布于:2024-06-14 ⋅ 阅读:(132) ⋅ 点赞:(0)

引言

在上一篇关于结构体的文章中我们知道,C语言除了提供了内置类型,还提供了了自定义类型,今天我们要学习的便是自定义类型中的联合体类型和枚举类型

联合体

1.联合体的定义

联合体是一种特殊的数据结构,允许我们在相同的内存位置存储不同的数据类型

和结构体一样,联合体也是由一个或者多个成员构成的,这些成员可以是不同的类型

但是编译器只为最大的成员分配足够的内存空间。联合体的特点是所有成员共用同一块内存空间

因此联合体也叫共用体

当我们给联合体的其中一个成员赋值时,其他成员的值也会发生改变

2.联合体的声明

联合体的结构类似于结构体,由关键字union和多个成员变量组成。格式如下:

union union_name {  
    data_type1 member1;  
    data_type2 member2;  
    // ...  
    data_typen membern;  
};

其中:

  • union_name 是联合体的名称
  • data_type1, data_type2, ..., data_typen 是联合体中各成员的数据类型
  • member1, member2, ..., membern 是联合体中的成员名称

2.1 普通联合体

union data
{
    char c;
    int i;
};

2.2 嵌套联合体

// 定义一个内嵌的联合体类型  
union InnerUnion 
{  
    int x;  
    float y;  
};  
  
// 定义一个包含内嵌联合体类型的外部联合体  
union OuterUnion 
{  
    char c;  
    union InnerUnion inner; //这里嵌套了InnerUnion  
};  

2.3 匿名联合体

匿名联合体允许在不定义联合体名称的情况下,创建一个联合体对象,这种联合体只能在其定义的代码块内使用一次

union
{
    char c;
    int i;
};

2.4 用typedef简化联合体

我们可以使用typedef简化联合体,减少代码量

typedef union Un1
{
    char c;
    int i;
}Un1;//这样子我们可以使用Un1代替union Un1

3.联合体的创建以及初始化

下面的代码是联合体的创建以及初始化:

union Un
{
	char c;
	int i;
};
int main()
{
	//联合体的初始化
	union Un un = { 0 };

	return 0;
}

ps:由于联合体的成员是共用同一块内存空间的,因此它的初始化不同于结构体,联合体的初始化只需要一个值

4.联合体的访问

我们使用点操作符(.),如下所示:

union Un
{
	int i;
	char c;
}un;

int main()
{
	union Un un = { 0 };
	un.c = 'c';
	printf("%c\n", un.c);
	un.i = 114514;
	printf("%d\n", un.i);
	return 0;
}

输出结果为:

c
114514

由于联合体的所有成员共用同一块内存空间,当我们对上面的代码进行调整:

union Un
{
	int i;
	char c;
}un;

int main()
{
	union Un un = { 0 };
	un.c = 'c';
	un.i = 114514;
	printf("%c\n", un.c);
	printf("%d\n", un.i);
	return 0;
}

这样子输出的结果可能就不是我们想要得到的结果

5.联合体的内存存储

5.1 联合体的大小

联合体的大小取决于其成员中最大的数据类型的大小。这是因为联合体中的所有成员都共享同一块内存空间,所以联合体的大小至少需要足够大以容纳其最大的成员

union Un 
{
    int i;
    char c;
};

int main() {
    printf("%zu\n", sizeof(union Un));
    return 0;
}

输出结果为:

4

联合体大小的计算有如下的规律:

1.联合体的大小至少是最大成员的大小

2.当最大成员大小不是最大对齐数的整数倍时,要对齐到最大对齐数的整数倍

5.2 联合体的存储

了解了联合体的大小,接下来我们来了解一下联合体的存储

示例1
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

代码如下:

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

在我之前的文章《C语言——数据在内存中的存储》,我们了解了计算机有关大小端的知识

在VS2022这一编译器中,数据在内存中的存储为小端存储

黄色部分为共有部分

绿色部分为非共有部分

示例3

代码如下:

union Un1
{
	char c;
	int i;
};
int main()
{
	printf("大小为%zu\n", sizeof(union Un1));
	return 0;
}

输出结果为:

大小为4

示例4

代码如下:

union Un
{
	short c[5];
	int i;
};
int main()
{
	printf("大小为%zu\n", sizeof(union Un));
	return 0;
}

输出结果为:

大小为12

  1. short大小为2,c中有5个大小为10,i大小为4,共用四个字节
  2. 最大对齐数为4,联合体大小为最大对齐数的整数倍,为12

6.联合体的使用示例

6.1 使用联合体判断大小端

我们之前学习数据在内存中的存储时,已经学习了一种可以判断大小端的方法,现在我们学习了联合体之后,可以使用联合体判断大小端

我们可以根据联合体的特性:联合体成员存储的起始位置相同 来实现取出第一位这一需求

代码如下:

int check_sys()
{
	union
	{
		char c;	
		int i;
	}u;
	u.i = 1;
	return u.c;
}
//如果是小端字节序 对i赋值时内存中是0x01 00 00 00
//读取c时读取前两个字节,即01
//如果是大端字节序 对i赋值时内存中是0x00 00 00 01
//读取c时读取前两个字节,即00

int main()
{
	int ret = check_sys();
	if (ret == 1)
		printf("小端\n");
	else
		printf("大端\n");
	return 0;
}

6.2 联合体的实际应用

使用联合体是可以节省空间的,例如:

当我们要搞一个活动,要上线一个礼品兑换单,礼品兑换单中有三种礼品:图书、杯子、衬衫

每一种礼品都有:库存量、价格、商品类型以及其他信息

图书:书名、作者、页数

被子:设计

衬衫:设计、可选颜色、可选尺寸

这一看,我们似乎可以使用结构体:

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;
};

枚举类型

C语言中的枚举类型是一种用户定义的数据类型,它允许你为整数值指定一个名称,从而增加代码的可读性。枚举类型通常用于表示一组固定的、命名的整数常量

7.枚举的定义

枚举类型通过关键字 enum来定义

在实际应用中我们经常把能够且便于一一列举的类型用枚举来表示,就比如一周的星期,一年的月份......

enum 枚举类型名

{

枚举常量1,

枚举常量2,

...

枚举常量n

};

8.枚举的声明

8.1 普通枚举

举个例子,我们可以使用枚举将一周的星期全部列出来

代码如下:

enum DAY
{
      MON=1, //指定从1开始,否则默认从0开始
      TUE,
      WED,
      THU, 
      FRI, 
      SAT, 
      SUN
};

8.2 匿名枚举

与匿名结构体和匿名联合体类似,枚举也有匿名枚举

例如这样:

enum
{
    zhangsan,
    lisi,
    wangwu
};

8.3 用typedef简化枚举类型

我们可以使用typedef简化枚举类型,减少代码量

如下所示:

typedef enum DAY
{
    MON = 1, //指定从1开始,否则默认从0开始
    TUE,
    WED,
    THU,
    FRI,
    SAT,
    SUN
}DAY;

9.枚举的创建和初始化

我们可以利用定义的枚举常量对枚举变量进行赋值

代码如下:

typedef enum Color 
{
    RED,
    GREEN,
    BLUE,
    YELLOW
}Color;



int main()
{
    for (int i = RED; i < YELLOW; i++)
    {
        printf("%d ", i);
    }
    return 0;
}

10.枚举常量的打印

我们来看一段代码:

typedef enum Color 
{
    RED,
    GREEN,
    BLUE,
    YELLOW
}Color;

int main()
{
    for (int i = RED; i <= YELLOW; i++)
    {
        printf("%d ", i);
    }
    return 0;
}

输出结果为:

0 1 2 3

观察结果我们可以知道:枚举成员是个常量,默认从零开始

11.枚举常量的大小

枚举常量在计算机中所占字节数是多少呢?

我们可以通过代码来查看一下:

typedef enum Color
{
    RED,
    GREEN,
    BLUE,
    YELLOW
}Color;

int main()
{
    printf("enum color: %d\n", sizeof(Color));
    return 0;
}

输出结果为:

enum color: 4

枚举类型的大小与int类型一样,都是4个字节

12.枚举类型的优点

我们可以使用 #define 定义常量,为什么要使用枚举

枚举的优点有:

1.增加代码的可读性和可维护性

2.和 #define 定义的标识符比较枚举有类型检查,更加严谨

3.便于调试,预处理阶段会删除 #define 定义的符号

4.使用方便,一次可以定义多个常量

5.枚举常量是遵循作用域规则的,枚举声明在函数内,只能在函数内使用

13.枚举类型的使用示例

enum  Color
{
	RED = 1,
	GREEN = 2,
	BLUE = 4
};

int main()
{
    enum Color clr = GREEN;  
    printf("The color is: %d\n", clr); 
  
    return 0;
}

这样子可以实验使用枚举常量给枚举变量赋值

结束语

磨磨蹭蹭写了很久,终于是写完了

希望看到的朋友们能点赞收藏加关注!!!


网站公告

今日签到

点亮在社区的每一天
去签到