探究C语言中的联合体与枚举:数据多面手的完美组合!

✨✨ 欢迎咱们来到贝蒂大讲堂✨✨

养成好习惯,先赞后看哦~

所属专栏:C语言学习
贝蒂的主页:Betty‘s blog

1. 联合体的界说

联合体又名共用体,它是一种特别的数据类型,答应您在相同的内存方位存储不同的数据类型。给联合体其中⼀个成员赋值,其他成员的值也跟着变化。

2. 联合体根底

2.1 联合体声明

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

union [union tag] {
member definition;
member definition;
member definition;
} [one or more union variables];

  • union tag 是你自己界说的,每个 member definition 是规范的变量界说,比方 int i; 或许 float f; 或许其他有效的变量界说。在共用体界说的结尾,最后一个分号之前,您能够指定一个或多个共用体变量,这一点和结构体类似。

(1) 普通联合体

union data
{
    int n;
    char ch;
};

(2) 嵌套联合体

联合体也是能够嵌套运用的。

union Un1
{
	char c[5];
	int i;
};
union Un2{
	int n;
	union Un1 u1;
};

(3) 匿名联合体

匿名联合体是一种特别联合体,省掉了联合体称号,这种联合体只能在其界说的代码块内运用一次。例如,假如你在一个函数内部界说了一个匿名联合体,则该联合体只能在该函数内部运用。当代码块履行结束后,该联合体将不再可见。

union
{
    int n;
    char ch;
};

(4) typedef联合体

咱们也能够运用typedef简化联合体。

typedef union Un1
{
	char c[5];
	int i;
}Un1;//之后能够运用Un1替代union Un1

2.2 联合体变量的创立与初始化

联合体变量创立除了在创立联合体时分界说,也能够在主函数内界说并且同时能够对齐初始化。

用例如下:

union Un
{
	char c;
	int i;
};
int main()
{
	//联合体的初始化
	union Un u1 = { 'a',0 };//过错
	union Un u2 = { 0 };//正确
	return 0;
}
  • 联合体的初始化只能运用一个值,由于联合体的所有成员同享同一块内存空间。

2.3 拜访联合体

为了拜访联合体的成员,咱们运用成员拜访运算符(.)。成员拜访运算符是联合体变量称号和咱们要拜访的共用体成员之间的一个句号。下面是一个实例:

#include<string.h>
typedef union Un1
{
	char c[10];
	int i;
}Un1;
int main()
{
         Un1 u = { 0 };
	printf("%d ", u.i);
	printf("%s ", strcpy(u.c, "abcdef"));
	return 0;
}

输出成果:

探究C语言中的联合体与枚举:数据多面手的完美组合!

3. 联合体的内存存储

3.1 联合体的巨细

联合体的巨细是其成员变量巨细之和,仍是和结构体一样遵从某种特别规则呢?咱们经过以下代码试验一下。

union Un
{
	char c[5];
	int i;
};
int main()
{
	union Un u2 = { 0 };
	printf("巨细为%zd", sizeof(union Un));
	return 0;
}

输出成果:

探究C语言中的联合体与枚举:数据多面手的完美组合!

经过验证咱们知晓联合体的巨细并不是其成员变量巨细之和,也是遵从某种特定的规则。

那么这种规则到底是什么呢?其实很简单

  • 联合的⼤⼩⾄少是最⼤成员的⼤⼩。
  • 当最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时分,就要对⻬到最⼤对⻬数的整数倍。
  • 对⻬数=编译器默许的⼀个对⻬数与该成员变量⼤⼩的较⼩值。(VS 中默许的值为 8 ,Linux中gcc没有默许对齐数,对⻬数便是成员⾃⾝的⼤⼩)

3.2 存储方式

知道了联合体的巨细,咱们也就会很简单知道它的内存存储方式了。下面有详细四个样例:

(1) 样例一

#include <stdio.h>
union Un
{
	char c;
	int i;
};
int main()
{
	//联合变量的界说
	union Un un = { 0 };
	// 下⾯输出的成果是⼀样的吗?
	printf("%pn", &(un.i));
	printf("%pn", &(un.c));
	printf("%pn", &un);
	return 0;
}

输出成果:

探究C语言中的联合体与枚举:数据多面手的完美组合!

  • 经过这次试验咱们联合体从起始方位开端共用的

(2) 样例二

#include <stdio.h>
//联合类型的声明
union Un
{
	char c;
	int i;
};
int main()
{
	//联合变量的界说
	union Un un = { 0 };
	un.i = 0x11223344;
	un.c = 0x55;
	printf("%xn", un.i);
	return 0;
}

输出成果:

探究C语言中的联合体与枚举:数据多面手的完美组合!

示意图:

探究C语言中的联合体与枚举:数据多面手的完美组合!

  • 蓝色为共用部分,绿色为非共用部分
  • VS编译器为小端存储

(3) 样例三

#include <stdio.h>
union Un1
{
	char c;
	int i;
};
int main()
{
	// 下⾯输出的成果是什么?
	printf("巨细为%dn", sizeof(union Un1));
	return 0;
}

输出成果:

探究C语言中的联合体与枚举:数据多面手的完美组合!

示意图:

探究C语言中的联合体与枚举:数据多面手的完美组合!

解析:

  1. c的巨细为一个字节,i的巨细为四个字节,他们共用一个字节。
  2. 最大对齐数为4,结构体巨细此刻刚好为4,是最大对齐数的整数倍。

(4) 样例四

#include <stdio.h>
union Un2
{
	short c[7];
	int i;
};
int main()
{
	// 下⾯输出的成果是什么?
	printf("巨细为%dn", sizeof(union Un2));
	return 0;
}

输出成果:

探究C语言中的联合体与枚举:数据多面手的完美组合!

示意图:

探究C语言中的联合体与枚举:数据多面手的完美组合!

解析:

  1. short巨细为2,c中有7个巨细为14,i巨细为4,共用四个字节。
  2. 最大对齐数为4,联合体巨细为最大对齐数的整数倍,为16。

4. 运用联合体判别巨细端

咱们早在学习数据在内存中怎么存储时就已经了解过一种判别巨细端的办法,今日就为咱们介绍另一种办法——经过联合体判别巨细端,

仍是这幅图,咱们要判别巨细端就需求判别榜首位存储到底是01仍是00。

探究C语言中的联合体与枚举:数据多面手的完美组合!

那怎么取出榜首位呢?除了经过指针,咱们也能运用联合体共用同一块内存这一性质判别。

代码如下:

int check_sys()
{
	union
	{
		int i;
		char c;
	}un;
	un.i = 1;
	return un.c; //回来1是⼩端,回来0是⼤端
}
int main()
{
	int ret = check_sys();
	if (ret == 1)
	{
		printf("⼩端n");
	}
	else
	{
		printf("⼤端n");
	}
	return 0;
}

5. 联合体的运用

经过联合体咱们能够节省一部分内存。比方:咱们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种产品:图书杯⼦衬衫。每⼀种产品都有:库存量、价格、产品类型和产品类型相关的其他信息。

其他信息:

图书:书名、作者、⻚数 杯⼦:规划 衬衫:规划、可选颜⾊、可选尺⼨

我榜首想法是经过一个结构体界说:

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

6. 枚举的界说

在 C 语言中,枚举(enum)是一种用户界说的数据类型,用于界说一个由标识符列表组成的整数常量调集。枚举类型经过关键字 enum来界说。

在实践运用中咱们常常把能够且便于一一列举的类型用枚举来表明。就比方:一周的星期、一年的月份……,其基本语法如下:

enum 枚举类型名 { 标识符1, 标识符2, … };

  • 枚举类型名受自己界说,如:week,year……,标识符便是其中的枚举常量,如Mon,Tues,Wed……
  • 每个枚举常量能够用一个标识符来表明,也可认为它们指定一个整数值,假如没有指定,那么默许从 0 开端递增。

7. 枚举根底

7.1 枚举的声明

(1) 普通枚举

接下来咱们举个例子,比方:一星期有 7 天,假如不用枚举,咱们需求运用 #define 来为每个整数界说一个别名:

#define MON  1
#define TUE  2
#define WED  3
#define THU  4
#define FRI  5
#define SAT  6
#define SUN  7

这个看起来代码量就比较多,接下来咱们看看运用枚举的方式:

enum DAY
{
      MON=1, //指定从1开端,不然默许从0开端
      TUE,
      WED,
      THU, 
      FRI, 
      SAT, 
      SUN
};

(2) 匿名枚举

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

enum
{
    APPLE,
    BANANA,
    ORANGE
};

(3) typedef枚举

咱们也能够运用typedef简化枚举。

typedef enum DAY
{
    MON = 1, //指定从1开端,不然默许从0开端
    TUE,
    WED,
    THU,
    FRI,
    SAT,
    SUN
}DAY;

7.2 打印枚举常量

typedef enum DAY
{
    MON, 
    TUE,
    WED,
    THU,
    FRI,
    SAT,
    SUN
}DAY;
int main()
{
    for (int i = MON; i < SUN; i  )
    {
        printf("%d ", i);
    }
    return 0;
}

输出成果:

探究C语言中的联合体与枚举:数据多面手的完美组合!

  • 这也间接证明枚举是一个常量,默许从0开端。

7.3 枚举变量的创立与初始化

咱们能够运用界说的枚举常量对枚举变量进行赋值。

typedef enum DAY
{
    MON, 
    TUE,
    WED,
    THU,
    FRI,
    SAT,
    SUN
}DAY;
int main()
{
    DAY a = MON;//最好用枚举常量赋值
    return 0;
}
  • 那是否能够拿整数给枚举变量赋值呢?在C语⾔中是能够的,可是在C 是不⾏的,C 的类型检查⽐较严格。

8. 枚举常量的巨细

枚举常量的巨细同 int 的巨细一样,都是四个字节。

咱们能够经过以下代码来试验:

#include <stdio.h>
enum color1
{
    RED,
    GREEN,
    BLUE
};
enum color2
{
    GRAY = 0x112233445566,
    YELLOW,
    PURPLE
};
int main()
{
    printf("enum color1: %dn", sizeof(enum color1));
    printf("enum color2: %dn", sizeof(enum color2));
    return 0;
}

输出成果:

探究C语言中的联合体与枚举:数据多面手的完美组合!

9. 枚举的长处

乍一看,咱们可能会感觉枚举有点弄巧成拙的感觉,那运用枚举到底有哪些长处呢?

长处:

  1. 添加代码的可读性和可维护性
  2. 和#define界说的标识符⽐较枚举有类型检查,愈加谨慎。
  3. 便于调试,预处理阶段会删去 #define 界说的符号
  4. 使⽤⽅便,⼀次能够界说多个常量
  5. 枚举常量是遵从作⽤域规则的,枚举声明在函数内,只能在函数内使⽤

10. 枚举的运用

枚举的运用常与switch语句联系起来。

#include <stdio.h>
int main()
{
    enum color { red = 1, green, blue };
    enum  color favorite_color;
    /* 用户输入数字来挑选色彩 */
    printf("请输入你喜爱的色彩: (1. red, 2. green, 3. blue): n");
    scanf("%d", &favorite_color);
    /* 输出成果 */
    switch (favorite_color)
    {
    case red:
        printf("你喜爱的色彩是红色n");
        break;
    case green:
        printf("你喜爱的色彩是绿色n");
        break;
    case blue:
        printf("你喜爱的色彩是蓝色n");
        break;
    default:
        printf("你没有挑选你喜爱的色彩n");
    }
    return 0;
}