当前位置: 首页 > news >正文

【C语言】19. ⾃定义类型:联合和枚举

文章目录

  • 一、联合体
    • 1、联合体类型的声明
    • 2、联合体的特点
    • 3、结构体和联合体的对⽐
    • 4、联合体⼤⼩的计算
    • 5、练习
  • 二、枚举
    • 1、枚举类型的声明
    • 2、枚举类型的使用
    • 3、枚举类型的优点

一、联合体

1、联合体类型的声明

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

但是编译器只为最⼤的成员分配⾜够的内存空间。联合体的特点是所有成员共⽤同⼀块内存空间。所以联合体也叫:共⽤体。
因此如果给联合体其中⼀个成员赋值,其他成员的值也会跟着变化。
关键词为union

接下来我们来计算一个联合体的大小:

#include<stdio.h>//联合体的声明
union Un
{char c;int i;
};int main()
{union Un un = { 0 };printf("%zd\n", sizeof(un));return 0;
}

运行结果:
在这里插入图片描述
可以看到结果为4,那么这个结果是怎么得出的呢?我们就要了解一下联合体的特点了。

2、联合体的特点

联合体的成员是共⽤同⼀块内存空间的,这样⼀个联合变量的⼤⼩,⾄少是最⼤成员的⼤⼩。

我们来观察一段代码:

#include<stdio.h>union Un
{char c;int i;
};
int main()
{union Un un = { 0 };printf("%p\n", &un);printf("%p\n", &(un.i));printf("%p\n", &(un.c));return 0;
}

运行结果:

可以看到联合体的成员地址都是一样的,说明他们的确是共用一块内存空间。

再观察一段代码:

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

调试观察:

可以看到我们将变量 i 的第四个字节的内容改为55,同样说明了联合体的成员是共用一块地址的。

因此我们可以画出un的内存分布图:

3、结构体和联合体的对⽐

我们再对⽐⼀下相同成员的结构体和联合体的内存布局情况。

结构体:

struct S
{char c;int i;
};struct S s = { 0 };

联合体:

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

画图演示:

4、联合体⼤⼩的计算

①:联合体的⼤⼩⾄少是最⼤成员的⼤⼩。
②:最⼤成员⼤⼩是最⼤对⻬数的整数倍。

例如:

#include<stdio.h>union Un1
{char c[5];//5   对齐数:1int i;    //4          4      
};union Un2
{short c[7];//14  对齐数:2int i;     //4          4
};int main()
{printf("%zd\n", sizeof(union Un1));//8printf("%zd\n", sizeof(union Un2));//16return 0;
}

运行结果:
在这里插入图片描述

那么我们使用联合体有什么作用呢?其实使⽤联合体是可以节省空间的。

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

如下;

上述结构设计时,我们可以把公共属性单独写出来,剩余属于各种商品本⾝的属性就使⽤联合体,这样就可以节省一部分内存空间。

如下:

#include<stdio.h>struct gift_list
{int stork_number;//库存量double author[20];//作者int num_pages;//页数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;
};int main()
{struct gift_list gl;gl.item.shirt.colors;//举例return 0;
}

5、练习

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

#include<stdio.h>int check_sys()
{int n = 1;return *(char*)&n;//取出内存中低位字节
}
int main()
{int ret = check_sys();if (ret == 1)printf("小端\n");elseprintf("大端\n");return 0;
}

运行结果:
在这里插入图片描述

二、枚举

1、枚举类型的声明

枚举顾名思义就是⼀⼀列举,把可能的取值⼀⼀列举。
⽐如我们现实⽣活中:
⼀周的星期⼀到星期⽇是有限的7天,可以⼀⼀列举。
性别有:男、⼥、保密,也可以⼀⼀列举。
⽉份有12个⽉,也可以⼀⼀列举。
三原⾊,也是可以一一列举。

那么这些数据的表⽰就可以使⽤枚举:

enum Day//星期
{Mon,Tues,Wed,Thur,Fri,Sat,Sun
};enum Sex//性别
{MALE,FEMALE,SECRET
};enum Color//颜色
{RED,//0GREEN,//1BLUE//2
};

以上定义的 enum Day , enum Sex , enum Color 都是枚举类型。
{ }中的内容是枚举类型的可能取值,也叫做枚举常量 。
这些枚举常量都是有值的,默认从0开始,依次递增。

#include<stdio.h>enum Color
{RED,GREEN,BLUE
};int main()
{printf("%d\n", RED);printf("%d\n", GREEN);printf("%d\n", BLUE);return 0;
}

运行结果:
在这里插入图片描述

同样在声明枚举类型的时候也可以赋初值。

#include<stdio.h>enum Color
{RED = 2,GREEN = 4,BLUE = 8
};int main()
{printf("%d\n", RED);printf("%d\n", GREEN);printf("%d\n", BLUE);return 0;
}

运行结果:
在这里插入图片描述

2、枚举类型的使用

那么枚举类型可以应用在哪里呢?我们可以用在计算器上,让枚举常量来代替数字,让代码更具有可读性。
例如:

#include<stdio.h>int Add(int x, int y)
{return x + y;
}int Sub(int x, int y)
{return x - y;
}int Mul(int x, int y)
{return x * y;
}int Div(int x, int y)
{return x / y;
}void calc(int(*pf)(int, int))
{int x, y, ret;printf("请输入两个操作数:\n");scanf("%d %d", &x, &y);ret = pf(x, y);printf("结果为%d\n", ret);
}void menu()
{printf("*************************\n");printf("*****  1.Add  2.Sub  ****\n");printf("*****  3.Mul  4,Div  ****\n");printf("*****    0.exit      ****\n");printf("*************************\n");
}enum Option
{EXIT,//0ADD,//1SUB,//2MUL,//3DIV//4
};int main()
{int input = 0;do{menu();printf("请选择: >");scanf("%d", &input);switch (input){case ADD:calc(Add);break;case SUB:calc(Sub);break;case MUL:calc(Mul);break;case DIV:calc(Div);break;case EXIT:printf("退出计算器...\n");break;default:printf("选择错误,请重新选择:\n");break;}} while (input != EXIT);return 0;
}

3、枚举类型的优点

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

枚举的优点:
①:增加代码的可读性和可维护性。
②:和#define定义的标识符⽐较,枚举有类型检查,更加严谨。
③:便于调试,预处理阶段会删除 #define 定义的符号(将符号直接变为常量)。
④:使⽤⽅便,⼀次可以定义多个常量。
⑤:枚举常量是遵循作⽤域规则的,枚举声明在函数内,只能在函数内使⽤。

相关文章:

  • 登录接口的密码进行RSA加密Java脚本
  • 牛客网NC218480统计正负数个数
  • VMware中快速安装与优化Ubuntu全攻略
  • 无人机避障——深蓝学院浙大Fast-planner学习部分(前端部分)
  • SpringBoot基础(静态资源导入)
  • 渗透测试流程-上篇
  • XBL6501/02/03在POE设备上的应用方案
  • 常用的关系性统计方法
  • 受控组件和非受控组件的适用场景分别是什么?
  • GRE作文总结和归纳
  • web第二次课后作业--设计一个注册登录系统
  • 涨薪技术|0到1学会性能测试第65课-SQL捕获阻塞事件
  • Vue3学习(组合式API——reactive()和ref()函数详解)
  • 实验5 DNS协议分析与测量
  • HttpServletRequest常用功能简介-笔记
  • SpringAOP
  • openEuler24.03 LTS下安装MySQL8.0.42
  • Linux 动态库热加载
  • 计量——异方差的检验及其修正
  • 报考叉车证需要参加哪些培训和考试?
  • 中国青年报:为见义勇为者安排补考,体现了教育的本质目标
  • 泽连斯基:乌代表团已启程,谈判可能于今晚或明天举行
  • “异常”只停留在医院里,用艺术为“泡泡宝贝”加油
  • 媒体:“西北大学副校长范代娣成陕西首富”系乌龙,但她的人生如同开挂
  • 巴方:印度上周导弹袭击造成至少40名平民死亡
  • 孙简任吉林省副省长