全网最细的自定义类型详解(结构体,枚举,联合),友友们快来接收吧

各位csdn的友友们肯定都掌握了c语言中char,short, int, long, float, double的类型,这些都是我们c语言中的一些内置类型,其实c语言是可以允许我们创造一些类型的,今天阿博就带领友友们一起掌握这些新的自定义类型😊😊😊

文章目录

  • 结构体
    • 1.结构体类型的声明
    • 2.结构的自引用
    • 3.结构体变量的定义和初始化
    • 4.结构体内存对齐
    • 5.结构体传参
    • 6.结构体实现位段(位段的填充&可移植性)
  • 枚举
    • 1.枚举类型的定义
    • 2.枚举的优点
    • 3.枚举的使用
  • 联合
    • 1.联合类型的定义
    • 2.联合的特点
    • 3.联合大小的计算

结构体

1.结构体类型的声明

2.结构的自引用

3.结构体变量的定义和初始化

4.结构体内存对齐

5.结构体传参

6.结构体实现位段(位段的填充&可移植性)

枚举

1.枚举类型的定义

2.枚举的优点

3.枚举的使用

联合

1.联合类型的定义

2.联合的特点

3.联合大小的计算

结构体类型的声明

struct stu
{
	char name[20];  //名字
	int  age;       //年龄
	char sex[5];    //性别
	char id[20];    //学号
};

注意最后那个分号千万不能丢哦!!!

这里的struct stu就是定义一个学生类型这里的name,age,sex,id就是成员变量,好了当我们了解这些后,就可以试着用结构体类型来创建变量了

struct stu
{
	char name[20];  //名字
	int  age;       //年龄
	char sex[5];    //性别
	char id[20];    //学号
}s4,s5,s6; 
int  main()
{
	struct stu s1;
	struct stu s2;
	struct stu s3;
}

友友们注意这里s1,s2,s3,s4,s5,s6都是我们用结构体创建出来的结构体变量,但是是s1,s2,s3是局部变量,s4,s5,s6是全局变量

结构体特殊的声明

struct
{
	char c;
	int a;
	double d;
}s1;
struct
{
	char c;
	int a;
	double d;
}*ps;
int  main()
{
	/*ps = &s1;*/   //error
	return  0;
}

以上两种结构体没有类型名,这就是传说中的匿名结构体了,友友们注意这种结构体定义出来的全局变量只能使用一次,还有虽然两个匿名结构体内容是相同的,但是我们也不能写*ps=&s1,因为在编译器看来,这两个地址类型是不一样的

结构体的自引用

/这是一个错误的示范
//struct Node
//{
//	int data;          //这里我们无法求得结构体的大小,它会无限循环下去
//	struct Node n;
//};
//int main()
//{
//	return  0;
//}
struct Node
{
	int data;   /*4*/
	struct Node* next;  /*4/8*/
};
int main()
{                        //正确的自引用方式
	struct Node n1;
	struct Node n2;
	 n1.next = &n2;
	 return  0;
}

在这里插入图片描述

typedef struct
{
	int data;
	char c;
}s;
typedef struct 
{
	int data;
	Node* next;    //error
}Node;
typedef struct Node
{
	int data;
	struct Node* next;   //correct
}Node;

注意typedef 是把struct 这个匿名结构体重新起名为s和Node,这里的s和Node是结构体类型名,不是定义的结构体变量!!!而且第二个typedef不能这样用,因为我们在没有重新命名Node之前,结构体变量中就使用了Node,这个顺序应放在命名之后使用!!!

结构体变量的定义和初始化

struct S
{
	int a;
	char c;
}s1;       //全局变量
struct S s3;     //全局变量
struct C
{
	float f;
	struct S s;
};
int main()
{
	struct S s2={100,'q'};   //局部变量
	struct C sc = { 3.14f,{200,'w'} };     //结构体嵌套初始化
	struct S s3 = { .c = 'a',.a = 150 };   //注意这里我们也可以不按照顺序进行初始化
	return 0;
}

友友们注意结构体初始化的时候没有我们想象的那么复杂,其实就是定义变量的同时赋初值,不同的结构体初始化的时候可能是不相同的,因为它们所包含的类型可能不一样,结构体嵌套初始化的时候就是在大括号里面在加一个大括号然后进行赋初值

结构体内存对齐

友友们,重点来了哈,来一起和阿博打起12.1分的精神拿捏它吧🦹‍♂️🦹‍♂️🦹‍♂️

struct S1
{
	int a;
	char c;
};
struct S2
{
	char c1;
	int a;
	char c2;
};
struct S3
{
	char c1;
	int a;
	char c2;
	char c3;
};
int main()
{
	printf("%d\n", sizeof(struct S1));
	printf("%d\n", sizeof(struct S2));
	printf("%d\n", sizeof(struct S3));
	return  0;
}

在这里插入图片描述
这里我们惯性思维会告诉我们是5, 6,7,但是当我生成结果的时候,可能会大吃一惊,下面来一起跟阿博探秘吧.这里阿博先给大家传输一些内功😁😁

1.结构体的第一个成员永远都放在0偏移处
2.从第二个成员开始,以后的每个成员都要对齐到某个对齐数的整数倍处,这个对齐数是成员自身大小和默认对齐数的较小值,在VS环境下默认对齐数为8,gcc环境下,没有默认对齐数,没有默认对齐数时,对齐数就是成员自身的大小.
3.当全部成员存放进去之后,结构体的大小必须是所有成员对齐数中最大对齐数的整数倍,如果不够,则浪费空间对齐.
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(包含嵌套结构体的成员中的最大对齐数)的整数倍.

在这里插入图片描述
这里可以给友友们测试下,在测试前先给友友们介绍一个函数offsetof
在这里插入图片描述
在这里插入图片描述

这里我们也可以看出它们的偏移量.
2.
在这里插入图片描述
3.
在这里插入图片描述
4.

友友们我们来个结构体嵌套来测试一下我们的实力,加油哦😊😊😊

在这里插入图片描述

这里友友们可能会想为什么存在内存对齐呢,这里阿博分两方面给友友们讲.
1.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的,某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常.
2.性能原因 :数据结构(尤其是栈)应该尽可能的在自然边界上对齐.原因在于,为了访问未对齐的内存,处理器需要做两次内存访问,而对齐的内存仅需要一次访问.
总体来说:结构体内存对齐是拿空间来换取时间的做法.这里阿博可以教大家一种既满足对齐,又节省空间的做法:让占用空间小的成员尽可能的集中在一起

这里给友友们看下图解哦
在这里插入图片描述

修改默认对齐数

#pragma pack(1)    //设置默认对齐数为8
struct S1
{
	char  c1;//1,1,1
	int i;   //4,1,1
	char c2;  //1,1,1    //默认是1,就相当于没有对齐,结果是6
};
#pragma pack()  //取消设置的默认对齐数,还原为默认

在这里插入图片描述

结论:结构在对齐方式不合适的时候,我们可以自己更改默认对齐数

结构体传参

struct   S
{
	int data[1000];
	int num;
};
struct S s = { {1,2,3,4},1000 };
//结构体传参
void print1(struct S s)
{
	printf("%d\n", s.num);
}
void print2(const struct S*ps)   //防止被修改
{
	printf("%d\n", ps->num);
}
int main()
{
	print1(s);  //传结构体
	print2(&s); //传地址
	return  0;
}

这里友友们注意我们首选print2函数,因为函数传参的时候,参数是需要压栈,会有时间和空间上的系统开销,如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降.所以结构体在传参的时候,要传结构体的地址.

结构体实现位段

什么是位段呢,可能会有很多友友疑问,因为我们都只知道段位😄😄😄,下面阿博就给大家普及一下
1.位段的成员通常是 int ,unsigned int ,signed int
2.位段的成员名后边有一个冒号和一个数字.

在这里插入图片描述
这里A就是一个位段.

//位段--二进制位
struct	A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};  //47 bit
int main()
{
	struct A sa = { 0 };
	printf("%d\n", sizeof(sa));
	return  0;
}

在这里插入图片描述

这里一共有47个比特位,按常理说6个字节,但为什么是8个字节呢,下面来和阿博一起探索吧

位段的内存分配

1.位段的空间上是按照需要以4个字节(int)或1个字节(char)的方式来开辟的
2.位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这里我们调试一下,发现和我们的假设一样,但是这只代表在vs上是这样,不代表其他编译器也是这样
位段的跨平台问题总结
1.int 位段被当成有符号数还是无符号数是不确定的
2.位段中最大的数目不能确定(16位机器最大16,32位机器最大32,写成27,在16位机器上就会出问题)

枚举类型的定义

enum Sex
{
	//枚举的可能取值 ,默认是从0开始,递增1的
	//枚举常量
	MALE=5,
	FAMALE,
	SECRET
};
int main()
{
	//enum Sex s=MALE;    //enum Sex=1;这里在c语言中是支持的,但是在c++中就不支持了
	printf("%d\n", MALE);
	printf("%d\n", FAMALE);  
	printf("%d\n", SECRET);
	return  0;
}

在这里插入图片描述

这里我们也可以改变它的初值,注意枚举常量是用逗号隔开的,最后一个逗号可以不加,枚举的好处可以让我们代码的可读性大大提高,枚举本身有类型检查,可以让我们的代码更加严谨,还便于我们代码的调试,一次可以定义多个常量

联合类型的定义

union Un
{
	char c;//1
	int i;//4
};
int main()
{
	union Un u;
	printf("%d\n", sizeof(u));
	printf("%p\n", &(u.c));
	printf("%p\n", &(u.i));
	printf("%p\n", &u);
}

在这里插入图片描述
在这里插入图片描述

联合体的应用

#include<stdio.h>
union  Un
{
	char c;
	int a;
};
int main()
{
	union Un u;
	u.a = 1;
	if (u.c == 1)
	{
		printf("小端\n");
	}
	else
		printf("大端\n");
	return   0;
}

在这里插入图片描述

联合体的特点:联合的成员是共用同一块内存空间的,这样一个联合变量的大小,至少是最大成员的大小,因为联合至少得有能力保存那个最大的成员

联合体大小的计算

1.联合的大小至少是最大成员的大小
2.当最大成员大小不是最大对齐数的整数倍的时候,就要对齐到最大对齐数的整数倍

union  Un
{
	char arr[5];   //5
	int n;        //4
};
int main()
{
	printf("%d\n", sizeof(union Un));
	return  0;
}

在这里插入图片描述

这里有5个char 的类型,大小是5,int 的大小是4,所以该联合体的大小至少是5,但是5不是4的整数倍,所以我们这里取了8个字节

这里给友友们出个题测试一下

union  Un
{
	short s[7];
	int n;
};
int main()
{
	printf("%d\n", sizeof(union Un));
	return  0;
}

这里有7个短整型,大小是14,有一个整形,大小是4,所以该联合体大小至少是14,但是14不是4的整数倍,所以需要往后浪费2个字节,所以大小是16

在这里插入图片描述
在这里插入图片描述

好了友友们,本期就到此结束了,如果你们感觉对自己有帮助的话,可以给阿博点个关注哦,后续阿博会继续给大家带来一些干货,下期再见💕💕💕

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/6392.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

[Java Web]Session | 一文详细介绍会话跟踪技术中的Session

⭐作者介绍&#xff1a;大二本科网络工程专业在读&#xff0c;持续学习Java&#xff0c;努力输出优质文章 ⭐作者主页&#xff1a;逐梦苍穹 ⭐所属专栏&#xff1a;Java Web 目录Session1、介绍2、工作流程3、工作原理4、基本使用5、Session的钝化与活化5.1、提出问题5.2、&…

【C语言蓝桥杯每日一题】—— 递增序列

【C语言蓝桥杯每日一题】—— 递增序列&#x1f60e;前言&#x1f64c;递增序列&#x1f64c;总结撒花&#x1f49e;&#x1f60e;博客昵称&#xff1a;博客小梦 &#x1f60a;最喜欢的座右铭&#xff1a;全神贯注的上吧&#xff01;&#xff01;&#xff01; &#x1f60a;作者…

软考中级-软件工程

1 软件过程1.1 能力成熟度模型&#xff08;CMM&#xff09;初始&#xff08;混乱&#xff09;->可重复&#xff08;建立基本、重复以往&#xff09;->已定义&#xff08;文档化、标准化&#xff09;->已管理&#xff08;制定产品质量标准&#xff09;->优化&#x…

HTML5 SSE

HTML5 服务器发送事件(Server-Sent Events) 服务器发送事件&#xff08;Server-sent Events&#xff09;是基于 WebSocket 协议的一种服务器向客户端发送事件和数据的单向通讯。 HTML5 服务器发送事件&#xff08;server-sent event&#xff09;允许网页获得来自服务器的更新。…

快速将PDF转换为图片:免费的在线PDF转换器

在现代数字时代&#xff0c;PDF是一种非常常见的文件格式。它们在学术界&#xff0c;商业领域和许多其他领域中被广泛使用。有时&#xff0c;您可能需要将PDF文件转换为图像格式&#xff0c;以便能够方便地与他人共享和使用。在这种情况下&#xff0c;您可以使用免费的在线PDF转…

PyCharm 配置sqlite3驱动

在PyCharm中可以查看sqlite3数据库&#xff0c;具体要如何做呢&#xff1f; 数据库入口 打开PyCharm&#xff0c; 在最右侧&#xff0c;有一个Database的表示&#xff0c;点击如下图所示。 如果没有找到这个选项&#xff0c; 点击View -> Tool Windows -> Database同…

chatgpt实际是怎样工作的?

文章翻译自&#xff1a; https://www.assemblyai.com/blog/how-chatgpt-actually-works/ ChatGPT 是 OpenAI 的最新语言模型&#xff0c;比其前身 GPT-3 有了重大改进。与许多大型语言模型类似&#xff0c;ChatGPT 能够为不同目的生成多种样式的文本&#xff0c;但具有更高的精…

MBD-有感(Hall)开环BLDC控制模型(下)

目录 前面 保护策略 DC_Bus_Measurements Protection_Check 外设配置 最后 前面 上一篇已经把霍尔有感BLDC开环控制模型的主要部分分析完成了 MBD-有感(Hall)开环BLDC控制模型&#xff08;上&#xff09; 语雀 这一篇分析一些边边角角&#xff0c;但不成体系的部分。…

全网最详细,Jmeter接口测试场景-万条测试数据校验结果,循环断言(案例)

目录&#xff1a;导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09;前言 在工作中&#xff0…

这个ChatGPT插件可以远程运行代码,还生成图表

ChatGPT的插件使数据科学成为一种简单、愉快的体验 我们做数据分析时一般都是使用这样的流程来进行&#xff1a;运行jupyter notebook、安装库、解决依赖关系和版本控制&#xff0c;数据分析&#xff0c;生成图表。ChatGPT的“Code Interpreter”插件可以帮助我们进行数据分析…

【Redis】五大数据结构及其常用指令

文章目录说明String类型List类型Set类型Hash类型Sorted Set 排序集合总结说明 Redis里面的数据类型有String、List、Set、Hash、Zset。这篇文章会介绍这5种数据类型并介绍操作它们的指令. String类型 字符串是一种最基本的Redis值类型。Redis字符串是二进制安全的&#xff0…

5.1.1 Ext JS之Grid actioncolumn动作列的动态显示

在Ext JS 的 Grid中添加动作列的方式是配置一个 actioncolumn类型的动作列,这个动作列可以包含多个图表的按钮。 添加的方式如下: {xtype: actioncolumn,items:[{iconCls: x-fa fa-trash,}]}在有的时候场景中, 会根据不同行的数据来决定是否显示动作按钮, 也就是最后的效果…

关于OpenAI的DALL的一点使用心得

文章目录注册DALL使用根据描述来generate上传图片来generate也可以根据描述信息或者相似的图片来进行设计注册DALL https://openai.com/product/dall-e-2 使用 根据描述来generate surprise me 自动生成描述&#xff08;因为每个月只有15个免费credits&#xff0c;节省起见…

java面试题(持续更新)

java面试题&#xff08;持续更新&#xff09; java 基础 java面向对象有哪些特征 面向对象的三大特征&#xff1a;封装、继承、多态 封装&#xff1a;隐藏了类的内部实现机制&#xff0c;可以在不影响使用的情况下改变类的内部结构&#xff0c;同时也保护了数据&#xff0c;…

Microsoft Dynamics 365 Business Central Planning Worksheet中Action Message状态变化

学习目标&#xff1a; 掌握Planning Worksheet中Action Message状态变化 学习内容&#xff1a; 掌握 创建物料&#xff0c;工作中心&#xff0c;工艺路线&#xff0c;BOM&#xff0c;物料和工艺路线&#xff0c;BOM的关联掌握 按订单的生产的物料卡片设置掌握 创建销售订单并…

二叉树的5个性质【要点:完全二叉树的性质】

只讲不会的 普通二叉树就要讲排列顺序了&#xff01;&#xff01;&#xff01; 预备&#xff1a;满二叉树&#xff1a;1.前提是它必须是二叉树 2.每个结点&#xff08;除了终端结点外&#xff09;都是2个子女。 要点1&#xff1a;关于普通的树的结点的计算&#xff0…

【CocosCreator入门】CocosCreator组件 | Label(文本)组件

Cocos Creator 是一款流行的游戏开发引擎&#xff0c;具有丰富的组件和工具&#xff0c;其中Label组件是最常用的之一。Label 组件是一个用于显示文本的 UI 组件。在本文中&#xff0c;我们将探讨 Label 组件的一些技术方面&#xff0c;包括如何创建、配置和使用它。 目录 一、…

java的集合体系结构(以及集合的遍历方式)

文章目录java集合的体系结构遍历方式通用(三种):迭代器,增强for,lambda表达式遍历迭代器(不依赖索引,适合set集合遍历)java集合的体系结构 注意点&#xff1a; Col1 ection是一个接口&#xff0c;我们不能直接创建他的对象。 所以&#xff0c;现在我们学习他的方法时&#xff0…

【数据库管理】①实例与数据库

1.Oracle RDBMS 架构图 2. Oracle 体系结构 由此区分database和instance的区别 No.1.oracle serverdatabase instance2.databasedata file、control file、redo log file3.instancean instance accesses a database4.oracle memorySGA PGA(oracle的内存结构)5.instanceSGA …

用C语言写一个函数,把字符串转换成整数

这是一个很有意思的问题。请不要把这个问题想的太简单了&#xff0c;考虑问题时应该尽可能的全面一些。请先思考并且实现这个函数&#xff0c;再来看讲解。 分析一下&#xff1a;函数名是StrToInt&#xff0c;那么可以这么调用&#xff1a; int ret StrToInt("1234&quo…