首页 > 其他分享 >【C语言】自定义类型——联合和枚举

【C语言】自定义类型——联合和枚举

时间:2024-07-23 23:30:03浏览次数:18  
标签:自定义 int 联合体 C语言 char 枚举 un printf

目录

一、联合体

1.1 联合体类型的声明

1.2 联合体的特点

1.2.1 特点1

1.2.2 特点2

1.2.3 特点3

1.3 联合体的大小

1.4 相同成员的结构体和联合体的对比

1.5 使用联合体节省空间的例子

1.6 运用联合体判断大小端

1.7 利用联合体打印存储的字节内容

二、枚举类型

2.1 枚举类型的声明

2.2 枚举类型的优点

2.3 枚举类型的使用


一、联合体

1.1 联合体类型的声明

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

声明方式如下:

#define _CRT_SECURE_NO_WARNINGS#include<stdio.h>
//联合类型的声明
union Un
{
char c;
int i;
};

那联合体和结构体究竟有什么区别呢??

下面将重点讲解联合体的特点!!

1.2 联合体的特点

1.2.1 特点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;
}

000000B31D0FF694

000000B31D0FF694

000000B31D0FF694

 

我们可以发现,三个地址打印出来是一样的。那既然都共用一块空间,那大小有多大呢??

1.2.2 特点2

编译器只为最大的成员分配⾜够的内存空间(因为联合体至少得有能力保护最大的那个成员)

//联合类型的声明
union Un
{
char c;
int i;
}
Jint main()
//联合变量的定义union Un un ={0 };
//计算连个变量的大小
printf("%d\n",sizeof(un));
return 0;
}

4

1.2.3 特点3

给联合体其中⼀个成员赋值,其他成员的值也跟着变化。

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

11223355

这里为什么打印出来的是11223355呢,我们根据3个特点,可以分析画出un的内部布局图

 

充分说明了特点3!

1.3 联合体的大小

特点2提到,编译器只为联合体最大的成员分配足够的空间,那联合体的大小就一定等于最大成员变量的大小吗??

答案是不对的,我们可以看看下面的代码

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

union Un2
{
short c[7];//14
int i;//4

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

8

16

 我们可以验证出,虽然编译器只为最大的成员分配足够空间,但不代表联合体的大小就是最大成员变量的大小!!!

联合体的大小要遵循以下两个特点:

1、联合的大小⾄少是最⼤成员的大小。

2、当最⼤成员大小不是最大对齐数的整数倍的时候,就要对⻬到最⼤对⻬数的整数倍。

这说明,联合体虽然可以节省空间,但也不是一味地节省,他也是有自己的对齐规则的。

分析上图代码:

Un1的第一个成员数组虽然是5个字节的大小,但是最大对齐数只能取char类型,所以是1,而int是4,所以Un1的最大对齐数是4,为了保证能放下5个字节的空间,所以最大对齐数翻倍变成8!

Un2的第一个成员数组虽然是14个字节的大小,但最大对齐数只能取short类型,所以是2,而int是4,所以Un2的最大对齐数是4,为了保证能放下14个字节的空间,所以最大对齐数翻4倍变成16!

1.4 相同成员的结构体和联合体的对比

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

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

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

 

这说明使用联合体是可以节省空间的!!!

1.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;//尺寸
};

但我们会发现,如果创建book变量,那设计、颜色、尺寸属性就会浪费掉。如果创建cup变量,那书名、作者、页数、可选颜色、尺寸属性就会浪费掉。如果创建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;
};

因为我们每个变量只使用一次,所以可以直接使用匿名结构体。

1.6 运用联合体判断大小端

int check_sys()
{
union
{
int i;
char c;
}un;
un.i = 1;
//如果是小端存储 01 00 00 00 此时因为c和i共用一块空间,c指向首字节,取c就是取01
//如果是大端存储00 00 00 01,取c就是取00
return un.c;//返回1是小端,返回0是大端
}

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

小端

1.7 利用联合体打印存储的字节内容

既然可以判断大小端,那其实也可以直接把存储的情况打印出来!!这是一个很神奇的代码!

union U
{
int i;//4
struct S
{
char c1:
char c2;
char c3;
char c4;
}s;//4
:
//因为恰好都是4个字节的大小,char1指向第一个字节,char2指向第二个字节,…以此类推
//所以我们只要将c1-c4打印出来,就可以知道内存的存储情况了,还可以顺便判断大小端。

int main()
{
union u un={0 };un.i = 0x11223344;//大端是11223344 小端是44332211
printf("%x %x %x %x\n",un.s.cl,un.s.c2,un.s.c3,un.s.c4);
}

44 33 22 11

 

二、枚举类型

2.1 枚举类型的声明

枚举顾名思义就是⼀⼀列举。

把可能的取值⼀⼀列举。

⽐如我们现实⽣活中:

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

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

⽉份有12个⽉,也可以⼀⼀列举

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

enum Day//星期
{
Mon,
Tues
Wed ,
Thur
Fri.
Sat,
Sun
};

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

enum Color//颜色
{
RED,
GREEN,
BLUE
};

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

这些可能取值本身都是由值的,默认是从0开始依次递增1,当然我们在声明的时候也可以自己赋初值,但是定义完成之后,就不能在该类型的外部去修改了!!

enum Sex//性别
{
MALE,
FEMALE,
SECRET
};
enum Color//颜色
{
RED,
GREEN ,
BLUE
};

int main()
{
RED = 10;//内部定义好后,外部不能再修改了
}

2.2 枚举类型的优点

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

比如我们在实现游戏时常常会这样去写

int main()
{
int input = 0;
srand((unsigned int)time(NULL));//时间函数,在入口设置一次就可以了
do
{
menu();
printf("请做出你的选择:");
scanf("%d",&input);
switch(input)
{
case 1:
  game();
  break;
}
case 0:
  printf("退出游戏\n");
  break;
default:
  printf("非法选择,请重新输入\n");
} 
}while (input);
return 0;
}

此时如果不和菜单建立联系

void menu
{
printf("**********************\n");
printf("**********************\n");
printf("********1.play********\n");
printf("********0.exit********\n");
printf("**********************\n");
printf("**********************\n");
}

我们并不能一下子就看出来case1和case0的含义,可读性较差,可如果在这边使用枚举类型,就可以增加代码的可读性,并且后期在维护的时候也方便。

int main()
{
int input = 0;
srand((unsigned int)time(NULL));//时间函数,在入口设置一次就可以
do
{
menu();
printf("请做出你的选择:scanf("%d",&input);
switch(input)
{
case play:
 game();
 break;
case exit:
 printf("退出游戏\n");
 break;
default:
 printf("非法选择,请重新输入\n");
} 
}while (input);
return 0;

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

#define定义的标识符是不过是一个符号,而枚举是一种类型,有类型检查写代码会更加严谨

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

 枚举类型在调试的时候会显示出成员名,但是#define就不会,标识符会直接替换成数字,后期如果需要调试找错误就不利于发现问题

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

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

枚举有作用域的概念,在一个函数内部使用,出了函数就不能用了,但是#define定义的标识符没有作用域概念,他是一个全局都可以使用的常量。

2.3 枚举类型的使用

enum Color//颜色
{
RED = 1,
GREEN = 2,
BLUE = 4
};
enum Color cLr = GREEN;

使用方法:使⽤枚举常量给枚举变量赋值

那是否可以拿整数给枚举变量赋值呢?

在C语⾔中是可以的,但是在C++是不⾏的,C++的类型检查⽐ 较严格。

 

标签:自定义,int,联合体,C语言,char,枚举,un,printf
From: https://blog.csdn.net/2302_81115588/article/details/140647737

相关文章

  • C语言课程设计:图书管理系统【源码+文档】
    【文章序言】:很高兴你能来阅读,博客分享日常编程,希望自己向着优秀程序员前行!博客来源于项目以及编程中遇到的问题总结,偶尔会有读书分享,我会陆续更新Java前端、后台、数据库、项目案例等相关知识点总结,感谢你的阅读和关注,希望我的博客能帮助到更多的人,分享获取新知,大家一起......
  • c语言的编译与调试
    1.GCC/G++gcc和g++是GNUCompilerCollection中的编译器,分别用于编译C和C++程序。它们的编译过程主要包括四个步骤:预处理(Pre-Processing)、编译(Compiling)、汇编(Assembling)、链接(Linking)。1.1编译过程预处理(Pre-Processing):gcc-Ehello.c-ohello.i对hello.c文件进行预处......
  • c语言实现猜数字游戏
    猜数字游戏是一种简单而有趣的编程实践项目,通常通过C语言来实现。 在这个游戏中,程序会在一个特定的范围内(例如100到200)随机生成一个数字作为要猜测的目标数字。玩家需要通过输入自己猜测的数字来尝试猜出这个目标数字。 每次玩家输入猜测后,程序会给出相应的提示,例如......
  • 【C语言基础习题】C语言练习题——bite 寒假班作业(4)
    C语言练习题——bite寒假班作业(4)题目第1题(单选题)题目名称:下面代码执行的结果是:()#include<stdio.h>intmain(){inti=0;for(i=0;i<10;i++){if(i=5)printf("%d",i);}return0;}题目内容:A.12345678910B.5555555555C......
  • 学习C语言第十一天(操作符)
    1.算术操作符/    整型的除法    1/2--->0        出不开得到商    浮点型除法    1.0/2--->0.5    保证至少有一个小数  %    计算整除之后的余数     %操作符两端必须是整数   *若要......
  • 学习C语言第十天(数组练习)
    一、三子棋game.h#define_CRT_SECURE_NO_WARNINGS#include<stdio.h>#include<time.h>#include<stdlib.h>#defineROW3#defineCOL3//初始化棋盘voidinitboard(charboard[ROW][COL],introw,intcol);//打印棋盘voiddispalyboard(charboard[ROW][CO......
  • mybatisPlus3.4 自定义sqlSessionFactory sql注入器失效、mybatis-plus批量插入报错In
    文章目录一、报错背景二、解决方法在mybatis-plus项目中集成自定义批量插入方法后报错。以下整理一下报错及解决方法。一、报错背景mybatis-plus是不提供insertList批量插入方法的,本人在自定义批量插入方法后,启动时正常,但是执行到insertList时报错。org.apache.i......
  • 用C语言打印杨辉三角形:**
    用C语言打印杨辉三角形:1.杨辉三角形规律:1.每行数字左右对称,由1开始逐渐变大,然后变小,回到1。2.第n行的数字个数等于n,第n行的第一个和最后一个数字都是1。3.对于第i行,除首尾两个1之外,任意位置的数等于它肩上的两个数之和。即第i行第j个数等于第i-1行第j-1个数与第i-1行第......
  • C语言输入输出函数
    输入函数1.scanf函数:用于格式化输入。例如:scanf("%d",&num);用于读取一个整数并存储到变量num中。输出函数:   1.printf函数:    用于格式化输出。例如:printf("Thenumberis%d\n",num);会输出指定的字符串和变量num     的值。   ......
  • C语言100道基础拔高题(1)
    1.有1,2,3,4这几个数字,问能组成多少个互不相同且无重复数字的三位数?    解题思路:首先输出由这几个数字所组成的所有三位数,接着再设置条件,使其输出的三位数不重复,下面我们来看下源代码。值得注意的是:所以题目的代码都是作者自行编写,如有更好的思路或者代码的优化,还请......