【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

02-26 1406阅读

【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

文章目录

  • 📝前言
  • 🌠 联合体类型的声明
    • 🌉联合体的特点
    • 🌠相同成员的结构体和联合体对⽐
      • 🌉联合体⼤⼩的计算
      • 🌠联合体应用
        • 🌉枚举类型的声明
        • 🌠枚举类型的优点
          • 🌉 枚举类型的使⽤
          • 🚩总结

            📝前言

            联合体(union)是允许一个变量通过不同的接口访问内存的一种数据类型,表示一个变量可以存储不同类型的值,而枚举是使用enum关键字定义一组相关且互斥的整形常量集合。本章阿森将和你学习联合体类型的声明,特点,有关大小的计算,还有枚举类型的声明,优点和使用。文章干货满满!学习起来吧😃!

            🌠 联合体类型的声明

            同结构体一样,声明结构体类型需要使用struct关键字,联合体则用union关键字。

            1. 包含对象名的声明方式:
            union 联合体名
            {
              类型 成员1;
              类型 成员2;
              ... 
              类型 成员n;
            }对象名;
            
            • 代码理解:
              #include 
              union S
              {
              	char c;
              	int a;
              }s1;
              int main()
              {
              	s1.c = 'a';
              	printf("%c\n", s1.c);
              	s1.a = 10;
              	printf("%d\n", s1.a);
              	return 0;
              }
              

              代码运行:

              【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

              1. 不包含对象名的声明格式:
              union 类型名 
              { 
                类型 成员1;
                类型 成员2;
                ... 
                类型 成员n;
              };
              
              • 代码实现:
                #include 
                union S
                {
                	char c;
                	int a;
                };
                int main()
                {
                	union S s2;
                	s2.c = 'b';
                	printf("%c\n", s2.c);
                	s2.a = 20;
                	printf("%d\n", s2.a);
                	return 0;
                }
                

                运行:

                【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

                🌉联合体的特点

                1. 编译器只为最⼤的成员分配⾜够的内存空间。联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共⽤体。

                例如:

                union u
                {
                	char c;
                	int u;
                };
                int main()
                {
                	union u uu;
                	printf("联合体uu的大小为%zd\n", sizeof(uu));
                	printf("   (uu)地址为%p\n", &uu);
                	printf("&(uu.c)地址为%p\n", &(uu.c));
                	printf("&(uu.u)地址为%p\n", &(uu.u));
                	return 0;
                }
                

                输出:

                【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

                图解:

                【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

                1. 联合的成员是共⽤同⼀块内存空间的,这样⼀个联合变量的⼤⼩,⾄少是最⼤成员的⼤⼩(因为联合⾄少得有能⼒保存最⼤的那个成员)。
                //联合类型的声明
                union u
                {
                	char c;
                	int i;
                };
                int main()
                {
                	//联合变量的定义
                	union u uu = { 0 };
                	un.i = 0x11223344;
                	un.c = 0x55;
                	printf("%x\n", uu.i);
                	return 0;
                }
                

                输出:

                【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

                图解:

                【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

                union定义了int和char两个成员,共享同一块内存空间,int类型占4个字节,低地址在前,高地址在后,char类型只占1个字节,存储在int的低地址字节。当执行:uu.i = 0x11223344时,此时int的4个字节分别存储如图,然后执行: uu.c = 0x55,由于VS是小端存储,低字节放在低地址处,char只占1个字节,它会覆盖int低地址的那个字节。所以int原来低地址字节0x44被覆盖为0x55。

                🌠相同成员的结构体和联合体对⽐

                结构体和联合体的主要区别在于:

                • 结构体中每个成员占用自己独立的内存空间,可以同时访问每个成员。
                • 联合体中所有成员共享同一块内存空间,只能同时访问其中一个成员。
                  1. 内存布局:

                    结构体中每个成员都有固定的偏移地址,占用独立的内存空间。

                    联合体中所有成员共享同一块内存,没有偏移地址,只能使用一个成员。

                  2. 访问成员:

                    结构体可以同时读取各个成员的值。

                    联合体只能访问当前使用的成员,其他成员的值将被覆盖。

                  3. 大小:

                    结构体的大小是所有成员大小的和。

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

                  • 结构体:
                    struct S
                    {
                     char c;
                     int i;
                    };
                    struct S s = {0};
                    
                    • 联合体:
                      union u
                      {
                      	char c;
                      	int i;
                      };
                      union u uu = { 0 };
                      

                      图解对比:

                      结构体S占用char + int+有可能开辟浪费的空间大小的内存,可以独立访问c和i,联合体u只占用int大小的内存,访问c或i时值会覆盖,结构体各成员独立,联合体成员共享同一内存空间。

                      【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

                      🌉联合体⼤⼩的计算

                      点击可以查看结构体的内存对齐规则——>【C语言】自定义类型:结构体深入解析(二)结构体内存对齐&&宏offsetof计算偏移量&&结构体传参

                      联合体大小计算规则:

                      1. 联合的⼤⼩⾄少是最⼤成员的⼤⼩。
                      2. 当最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时候,就要对⻬到最⼤对⻬数的整数倍。
                      • 来代码理解:
                        union Un1
                        {
                            char c[5];
                            int i;
                        };
                        union Un2
                        {
                            short c[7];
                            int i;
                        };
                        int main()
                        {
                            printf("%zd\n", sizeof(union Un1));//8
                            printf("%zd\n", sizeof(union Un2));//16
                            return 0;
                        }
                        

                        运行:

                        【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

                        图解分析:

                        【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

                        首先看union Un1如果联合体的大小是最大成员的最大成员的的大小,在联合体union Un1中,char[5]的大小理应是5,那计算的结果不是5。为什么是8呢?这是因为它完成了对齐的操作,如果是数组,是按元素类型大小来算他的对齐数的。char 元素的类型大小是1,VS默认对齐数是8,对齐数是8,i 的大小是4,VS默认对齐数是8,对齐数是4,接下来(4>1)整个联合体的对齐数是4,根据当最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时候,就要对⻬到最⼤对⻬数的整数倍。此时最大成员大小是数组char [5]大小为5,5不是4的整数倍,8(4*2)是4的整数倍。是不是真的是这样呢?会不会是偶然呢?

                        接下来我们看第二组:union Un2首先short c[7]是数组,总大小为14,然后由于数组是按照元素的类型大小来算对齐数,类型为short类型大小为2,VS默认对齐数为8,对齐数为2(2【C语言】自定义类型:结构体深入解析(二)结构体内存对齐&&宏offsetof计算偏移量&&结构体传参

                        🌠联合体应用

                        使⽤联合体是可以节省空间的:

                        ⽐如,我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯⼦、衬衫。

                        每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。

                        图书:书名、作者、⻚数

                        杯⼦:设计

                        衬衫:设计、可选颜⾊、可选尺⼨

                        结构体表示:

                        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. 第一种方法:
                        int check_sys()
                        {
                        	int n = 1;//01 00 00 00     00 00 00 01
                        	return *(char*)&n;
                        }
                        int main()
                        {
                        	int ret = check_sys();
                        	if (ret == 1)
                        		printf("小端\n");
                        	else
                        		printf("大端\n");
                        	return 0;
                        }
                        

                        VS运行:

                        【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

                        1. 第二种联合体巧妙方法:
                        int check_sys()
                        {
                        	union
                        	{
                        		char c;
                        		int i;
                        	}u;
                        	u.i = 1;
                        	return u.c;//返回1是⼩端,返回0是⼤端
                        }
                        

                        VS运行:

                        小端
                        

                        图解:

                        【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

                        大端存储:是指数据的低位字节内容保存在内存的高地址处,而数据的高位位字节内容,保存在内存的低地址处。

                        小端存储:是指数据的低位字节内容保存在内存的低地址处,而数据的高位字节内容,保存在内存的高地址处。

                        如果01是低位字节存储到低地址c时,是小端存储,如果01低位字节存储到高地址处,没有存储到c的位置,那么c的位置存储着00,返回为0,是大端存储。

                        🌉枚举类型的声明

                        枚举类型(enum)是一种特殊的类型,它可以为一组相关的常量值赋予用户定义的名称。

                        —>简单来说:枚举顾名思义就是⼀⼀列举。

                        枚举类型的声明语法:

                        enum 标识符 
                        {
                           枚举常量1, 
                           枚举常量2,
                           ...
                        } 变量;
                        
                        1. enum 关键字声明这是一个枚举类型。

                        2. 标识符是枚举类型的名称。

                        3. 在大括号{}内列出枚举类型的多个枚举常量,用逗号分隔。

                        4. 变量是枚举类型的变量,可以直接使用枚举类型名或枚举常量初始化。

                        例如:

                        enum Color //Color是枚举类型名
                        {
                        	RED,     // 枚举常量  
                        	GREEN,
                        	BLUE
                        } color;///color是Color类型的变量
                        int main()
                        {
                        	printf("%d\n", RED);
                        	printf("%d\n", GREEN);
                        	printf("%d\n", BLUE);
                        	return 0;
                        }
                        

                        输出:

                        【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

                        • 枚举常量默认从0开始依次累加1。也可以手动为枚举常量赋值:

                          例如:

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

                          运行结果:

                          【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

                          • 当然第一个元素未被赋值,给其它的常量赋值,该常量前面的值是默认值(0,1,2)后面递增1。

                            例如:

                            enum Color
                            {
                            	RED,
                            	white,
                            	GREEN = 8,
                            	BLUE ,
                            	BLACK,
                            };
                            int main()
                            {
                            	printf("%d\n", RED);
                            	printf("%d\n", white);
                            	printf("%d\n", GREEN);
                            	printf("%d\n", BLUE);
                            	printf("%d\n", BLACK);
                            	return 0;
                            }
                            

                            输出:

                            【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

                            🌠枚举类型的优点

                            为什么使⽤枚举?

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

                            枚举的优点:

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

                              如:之前的扫雷中可以这样定义用PLAY代替1,EXIT代替0,更具有个性化:

                            #include  
                            #include 
                            // 定义游戏选择枚举类型
                            enum Game_Selection
                            {
                                EXIT, // 退出游戏
                                PLAY  // 开始游戏
                            };
                            // 打印菜单
                            void menu()
                            {
                                printf("********** Menu **********\n");
                                printf("PLAY - Start the game\n");
                                printf("EXIT - Exit the game\n");
                                printf("********** Menu **********\n");
                            }
                            int main()
                            {
                                enum Game_Selection input; // 声明游戏选择变量
                                char choice[10]; // 声明选择输入缓冲区
                                do
                                {
                                    menu(); // 调用菜单函数
                                    printf("Please enter your choice: ");
                                    scanf("%s", choice); // 读取选择输入
                                    getchar(); // 清除输入缓冲区
                                    if (strcmp(choice, "PLAY") == 0)
                                    {
                                        input = PLAY; // 设置选择为开始游戏
                                    }
                                    else if (strcmp(choice, "EXIT") == 0)
                                    {
                                        input = EXIT; // 设置选择为退出游戏
                                    }
                                    else
                                    {
                                        printf("输入错误,请重新输入\n");
                                        continue; // 输入错误,继续循环
                                    }
                                    switch (input)
                                    {
                                    case PLAY:
                                        printf("扫雷游戏启动!\n");
                                        break;
                                    case EXIT:
                                        printf("不玩了,启动不了!\n");
                                        break;
                                    }
                                } while (input != EXIT);
                                return 0;
                            }
                            

                            代码运行:

                            【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

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

                            2. 便于调试,预处理阶段会删除 #define 定义的符号【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

                            3. 使⽤⽅便,⼀次可以定义多个常量

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

                            🌉 枚举类型的使⽤

                            那是否可以拿整数给枚举变量赋值呢?在C语⾔中是可以的,但是在C++是不⾏的,C++的类型检查⽐较严格。

                            • 在C语言中,枚举类型实际上就是整数类型,编译器会把枚举常量替换成对应的整数值。所以可以用整数直接给枚举变量赋值。

                            • 而在C++中,枚举类型是完全独立的类型。编译器会检查类型是否匹配,不允许用整数直接给枚举变量赋值。

                              例如:

                              # define _CRT_SECURE_NO_WARNINGS 1
                              #include 
                              #include 
                              //C语言
                              enum Color//颜⾊
                              {
                              	RED = 1,
                              	GREEN = 2,
                              	BLUE = 4
                              };
                              int main()
                              {
                              	enum Color c;
                              	c = 1; // 可以直接赋值整数
                              	return 0;
                              }
                              

                              输出:

                              【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

                              // C++语言
                              enum Color//颜⾊
                              {
                              	RED = 1,
                              	GREEN = 2,
                              	BLUE = 4
                              };
                              Color c;
                              c = 1; // 错误,类型不匹配
                              

                              输出:

                              【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

                              总结:

                              C语言中枚举类型实际上就是整数,允许用整数直接赋值

                              C++中枚举类型是独立类型,不允许用整数直接赋值,需要强制类型转换


                              🚩总结

                              这次阿森和你一起学习联合体类型的声明,特点,然后进行相同成员的结构体和联合体对⽐,⼤⼩的计算,联合体应用,枚举类型的声明,优点和扫雷改造使⽤方法,阿森将下一节和你一起学习动态内存管理🚀 。

                              感谢你的收看,如果文章有错误,可以指出,我不胜感激,让我们一起学习交流,如果文章可以给你一个小小帮助,可以给博主点一个小小的赞😘

                              【C语言】一篇文章深入解析联合体和枚举且和结构体的区别

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]