⾃定义类型:联合和枚举

前言:通过前文,我们系统学习了结构体的相关知识,深入理解了自定义类型的概念与实践,承接上文,我们将继续学习新的自定义类型:联合和枚举。

一、联合体

        像结构体⼀样,联合体也是由⼀个或者多个成员构成,这些成员可以不同的类型。但是编译器只为最⼤的成员分配⾜够的内存空间。联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共⽤体。

 1.联合体类型的声明

联合体的语法结构如下:

        

union   tag    (关键字:union  +   联合体名: tag)

{

       member-list;   //成员列表

};  

        

温馨提示:末尾分号不要忘记。

形如:

union Un
{
	char a;  
	int i;    
};

        

2.联合体的特点

        

       对于联合体而言,其最大的特点在于,共用一块内存,通过下列代码演示,我们能够充分的了解到这个特点。

union Un
{
	char a;  //1
	int i;    //4
};

int main()
{
    union Un u={0};
    // printf("%zd",sizeof(u));
	printf("%p\n", &u);
	printf("%p\n", &(u.a));
	printf("%p\n", &(u.i));
    return 0;
}

通过打印其地址,验证了它们共用同一块空间。

        

union Un
{
	char a;  //1
	int i;    //4
};

int main()
{
   	union Un u = { 0 };
	u.i = 0x11223344;
	u.a = 0x55;
	printf("%x\n", u.i);
    return 0;
}

①通过先对联合体中的 i 赋值,如下图所示:

②再对联合体中的 a 进行赋值,将覆盖 i 的一个字节大小的值。

        

③ 如下图所示:

        

        

进行调试也能发现:

        

        

        

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

        

        

两者主要的区别在于其空间的分配:

        

        对于结构体而言,其存在内存对齐规则,而对于联合体而言,其存在共用内存的规则

        

        

4.联合体大小的计算

        区别于结构体的内存大小计算方式,编译器只为最⼤的成员分配⾜够的内存空间,其中联合体的成员共用一块内存空间,联合体大小的计算需满足以下两个规则:

①:联合体的⼤⼩⾄少是最⼤成员的⼤⼩。

        

②:当最⼤成员⼤⼩不是最⼤对⻬数的整数倍的时候,就要对⻬到最⼤对⻬数的整数倍。

        

union Un
{
	char a;  //1
	int i;    //4
};

int main()
{
    union Un u={0};
    printf("%zd",sizeof(u));
    return 0;
}

由于 int 类型变量占用4个字节大小的空间   char类型变量占用1个字节大小的空间

        

所以整个联合体取决于int类型的内存大小。

        

但也存在一定的特殊性,出现最大内存成员不是最大对齐数的整数倍时,则需对齐到最大对齐数的整数倍。

比如:

        

#include <stdio.h>
union Un1
{
  char c[5];  //1  8  1
  int i;  // 4  8  4
};

union Un2
{
  short c[7];  //2  8   2
  int i; // 4   8   4
};

int main()
{
  //下面输出的结果是什么?
  printf("%d\n", sizeof(union Un1));
  printf("%d\n", sizeof(union Un2));
  return 0;
}

①:对于联合体Un1 ,其内存最大成员为char c[5] ,总共占5个字节大小,但其成员中的最大对齐数为4 ,出现了内存最大成员不为最大对齐数的整数倍,这时就需要对齐到最大内存的整数倍,则联合体的内存大小为8。

        

②:对于联合体Un2,其内存最大成员为short c[7] ,总共占14个字节大小,但其成员中的最大对齐数为4,出现了内存最大成员成员不是最大对齐数的整数倍时,这时就需要对齐到最大内存的整数倍,则该联合体的内存大小为16个字节。

        

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;//尺⼨
}

        若仅通过结构体将其所有属性列出的话,我们会发现在描述书本时,向 设计 颜色 尺寸 这类属性将用不到,此时就导致了内存浪费,同理在在描述 杯子 和  衬衫 的时候也会有部分属性用不到,这时我们就要思考,如何既做到不浪费空间,又能实现所需目的,对此而言,联合体是一个很好的选择。      

        

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

// 定义商品类型常量(方便识别)
#define TYPE_BOOK 0
#define TYPE_MUG 1
#define TYPE_SHIRT 2

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

// 打印礼品信息的函数
void print_gift(struct gift_list gift)
{
    printf("库存量:%d,定价:%.2f,类型:", gift.stock_number, gift.price);

    // 根据 item_type 判断具体类型并打印特有属性
    switch (gift.item_type)
    {
        case TYPE_BOOK:
            printf("书籍\n");
            printf("  书名:%s\n  作者:%s\n  页数:%d\n",
                gift.item.book.title,
                gift.item.book.author,
                gift.item.book.num_pages);
            break;

        case TYPE_MUG:
            printf("马克杯\n");
            printf("  设计:%s\n", gift.item.mug.design);
            break;

        case TYPE_SHIRT:
            printf("衬衫\n");
            printf("  设计:%s\n  颜色数量:%d\n  尺寸数量:%d\n",
                gift.item.shirt.design,
                gift.item.shirt.colors,
                gift.item.shirt.sizes);
            break;

        default:
            printf("未知类型\n");
    }
}

int main() 
{
    // 创建一个书籍类型的礼品
    struct gift_list book_gift =
    {
        .stock_number = 50,
        .price = 39.9,
        .item_type = TYPE_BOOK
    };
    strcpy(book_gift.item.book.title, "C语言入门");
    strcpy(book_gift.item.book.author, "张三");
    book_gift.item.book.num_pages = 300;

    // 创建一个衬衫类型的礼品
    struct gift_list shirt_gift =
    {
        .stock_number = 20,
        .price = 99.0,
        .item_type = TYPE_SHIRT
    };
    strcpy(shirt_gift.item.shirt.design, "卡通图案");
    shirt_gift.item.shirt.colors = 3;  // 3种颜色可选
    shirt_gift.item.shirt.sizes = 4;   // 4种尺寸可选

    // 打印礼品信息
    print_gift(book_gift);
    printf("-------------------\n");
    print_gift(shirt_gift);

    return 0;
}

        通过在联合体中,再用结构体描述其特有的属性,就能够做到,在描述一种商品时,不出现空间浪费,而且还能做到,商品的信息独立,比如:在描述书本时就会覆盖 杯子衬衫 的所存储在内存中的内容。       

        

练习二:

        写⼀个程序,判断当前机器是⼤端?还是⼩端

 ①原始方法:

        通过用char * 的指针,指向一个大小为 1 的整形变量 ,解引用就能访问其第一个字节,若第一个字节为1则说明其是小端存储,反之若第一个字节为0则说明其是大端存储。   

int check()
{
	int i = 1;
	char* p = (char *)&i;    //0x 01 00 00 00
	return *p;
}


int main()
{
	if (check())
	{
		printf("小端存储");
	}
	else
	{
		printf("大端存储");
	}
	return 0;
}

        

利用联合体: 将整形变量和字符变量放在同一个联合体,赋予整型变量的值为1,通过访问字符变量,就能得到整形变量中的第一个字节若第一个字节为1则说明其是小端存储,反之若第一个字节为0则说明其是大端存储。   

        

#include<stdio.h>
union Un
{
    char c;
    int i;
};

void check()
{
    union Un u={0};
    u.i=1;
    if(u.c==1)
    {
       printf("小端存储"); 
    }
    else
    {
        printf("大端存储");
    }
}

int main()
{
    check();
    return 0;
}

        

二、枚举

        

 1.枚举变量的声明

        枚举顾名思义就是⼀⼀列举,把可能的取值⼀⼀列举。

         

语法结构如下:

        

enum  tag (关键字:enum +  变量名 :tag)

{

        枚举常量,

        枚举常量,

        枚举常量

};       

        

温馨提示:末尾分号别忘记

        

⽐如我们现实⽣活中:


①:⼀周的星期⼀到星期⽇是有限的7天,可以⼀⼀列举

        
②:性别有:男、⼥、保密,也可以⼀⼀列举

        
③:三原⾊,也是可以意义列举

        

enum Day//星期
{
	Mon,  //0
	Tues, //1
	Wed,  //2
	Thur, //3
	Fri,  //4
	Sat,  //5
	Sun   //6
};

enum Sex//性别
{
  MALE,   //0
  FEMALE, //1
  SECRET  //2
};

enum Color//颜⾊
{
	RED,  //0
	GREEN, //1
	BLUE   //2
};

        

以上定义的 enum Day , enum Sex , enum Color 都是枚举类型。

        
其中{ }中的内容是枚举类型的可能取值,也叫 枚举常量 。

        
这些可能取值都是有值的,默认从0开始,依次递增1,当然在声明枚举类型的时候也可以赋初值。

        

例如:

        

enum Color//颜⾊
{
  RED=2,
  GREEN=4,
  BLUE=8
};

        

        

如果枚举常量中的一个值改变,其后的枚举常量也会跟着该改变。

        

例如:

        

enum Color//颜⾊
{
   RED,
   GREEN=5,
   BLUE
};

此时枚举常量RED的值为0,GREEN的值为5,BLUE的值为6

        

2.枚举的优点

        我们通常使用 #define 来定义常量,为什么要用枚举呢?

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

        对于#define 定义的常量,需要进行单独修改,而通过枚举定义的枚举常量,可以做到成群修改。


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

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

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

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

        

3.枚举的应用

        

练习一:

        比如要实现这样一个目的:我们要搞⼀个活动,要上线⼀个礼品兑换单,礼品兑换单中有三种商品:图书、杯⼦、衬衫。
每⼀种商品都有:库存量、价格、商品类型和商品类型相关的其他信息。

        

其中三种商品也有自己特殊的属性:

        

①图书:书名、作者、⻚数

        
②杯⼦:设计

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

        

#define  _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

// 定义商品类型常量(方便识别)
//#define TYPE_BOOK 0
//#define TYPE_MUG 1
//#define TYPE_SHIRT 2

//利用枚举修改define 定义的常量
enum TYPE
{
    TYPE_BOOK,
    TYPE_MUG,
    TYPE_SHIRT
};


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

// 打印礼品信息的函数
void print_gift(struct gift_list gift)
{
    printf("库存量:%d,定价:%.2f,类型:", gift.stock_number, gift.price);

    // 根据 item_type 判断具体类型并打印特有属性
    switch (gift.item_type)
    {
        case TYPE_BOOK:
            printf("书籍\n");
            printf("  书名:%s\n  作者:%s\n  页数:%d\n",
                gift.item.book.title,
                gift.item.book.author,
                gift.item.book.num_pages);
            break;

        case TYPE_MUG:
            printf("马克杯\n");
            printf("  设计:%s\n", gift.item.mug.design);
            break;

        case TYPE_SHIRT:
            printf("衬衫\n");
            printf("  设计:%s\n  颜色数量:%d\n  尺寸数量:%d\n",
                gift.item.shirt.design,
                gift.item.shirt.colors,
                gift.item.shirt.sizes);
            break;

        default:
            printf("未知类型\n");
    }
}

int main() 
{
    // 创建一个书籍类型的礼品
    struct gift_list book_gift =
    {
        .stock_number = 50,
        .price = 39.9,
        .item_type = TYPE_BOOK
    };
    strcpy(book_gift.item.book.title, "C语言入门");
    strcpy(book_gift.item.book.author, "张三");
    book_gift.item.book.num_pages = 300;

    // 创建一个衬衫类型的礼品
    struct gift_list shirt_gift =
    {
        .stock_number = 20,
        .price = 99.0,
        .item_type = TYPE_SHIRT
    };
    strcpy(shirt_gift.item.shirt.design, "卡通图案");
    shirt_gift.item.shirt.colors = 3;  // 3种颜色可选
    shirt_gift.item.shirt.sizes = 4;   // 4种尺寸可选

    // 打印礼品信息
    print_gift(book_gift);
    printf("-------------------\n");
    print_gift(shirt_gift);

    return 0;
}

        

定义商品类型常量(方便识别)
#define TYPE_BOOK 0

#define TYPE_MUG 1
#define TYPE_SHIRT 2

改进:利用枚举修改define 定义的常量
enum TYPE
{
    TYPE_BOOK,
    TYPE_MUG,
    TYPE_SHIRT
};

        

通过将#define定义的常量封装到枚举变量中,便于维护和使用。

既然看到这里了,不妨点赞和评论一下吧!

评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值