自定义数据类型(结构体、枚举)
创始人
2025-05-30 06:19:16

各位朋友,大家好。欢迎大家来到我的博客,我今天将要为大家分享的是自定义数据类型中的结构体和位段方面的知识。那么,话不多说,接下来就来看看我的分享吧。

目录

什么是结构体呢?

结构体的自引用

计算结构体的大小(对齐数)

位段 

什么是位段

 枚举

枚举的优点

总结


什么是结构体呢?

我们都知道数组,我们可以在数组中存放相同数据类型的数据,比如说整型数组里存放的都是整形,字符数组里存放的都是字符,但是,当我们想要存放不同数据类型的时候,比如说一个学生的姓名,年龄,性别,电话号码,我们该怎么做呢?这时候就体现到自定义数据类型:结构体的作用了。我们可以在结构体中存放我们想要存放的数据类型,这就极大的方便了我们的日常使用。

那么我们先来通过一个简单代码,来看看结构体是怎样定义和使用的吧。

#includestruct Stu
{char name[20];int age;char sex[10];char number[11];
}S;int main()
{S = { "张三",18,"female","12345678901" };return 0;
}

struct是关键字,而struct tag则是结构体类型 ,member-list是成员变量,variable-list是一个结构体变量,它属于全局变量,当我们初始化的时候我们可以像上面那样按顺序初始化,也可以像这样不需要严格的按顺序来初始化。

#includestruct Stu
{char name[20];int age;char sex[10];char number[11];
};int main()
{
//通过.来访问结构体成员变量struct Stu S = { .age = 18,.sex = "female",.name = "张三",.number = "12345678901" };return 0;
}

并且这里得注意,当我们在给结构体成员变量赋值的时候,前面必须得加上结构体数据类型,否则会报错。就像这样:

#includestruct Stu
{char name[20];int age;char sex[10];char number[11];
} S;int main()
{S = { .age = 18,.sex = "female",.name = "张三",.number = "12345678901" };return 0;
}

在声明结构体类型的时候,还有一种特殊的声明:不完全声明,也叫匿名结构体

struct
{char name[20];int age;char sex[10];char number[11];
} S;

这个结构体因为省略了tag,所以我们不知道他的数据类型,所以如果我们要在使用的时候,就需要在定义的时候就初始化,否则我们就找不到这个结构体了。

#includestruct
{int a;int b;
}x;struct
{int c;int d;
} *p;int main()
{p = &x;
}

 这个代码就是典型的匿名结构体不知道结构体类型的错误。

结构体的自引用

当我们知道了结构体之后,那么结构体有什么作用呢?结构体通常被使用在链表当中,链表中分为数据域跟指针域,数据域中用来数据,指针域中用来存放地址。所以这就需要我们使用结构体来存放不同类型的变量。但是我们这样写可以吗?

struct ListNode
{int data;struct ListNode next;
};

 这样很显然是不能达到我们的目的的,因为这里struct ListNode并没有定义结束,我们在这里使用的时候可能会报错。正确的定义方法应该是这样的:

struct ListNode
{int data;struct ListNode* next;
};

我们定义一个结构体指针,这个结构体指针用来存放另一个结构体的地址。

并且我们知道,在函数传参的时候,我们可以传值,也可以传址。那么当我们的参数是结构体的时候我们是传值好一些还是传地址好一些呢?

#includestruct S
{int data[1000];int num;
};void Print1(struct S s)
{printf("%d\n", s.num);
}void Print2(struct S* s)
{printf("%d\n", s->num);
}int main()
{struct S s = { {1,2,3,4,5},10 };Print1(s);Print2(&s);return 0;
}

是Print1好一些还是Print2好一些呢?我们都知道当我们传入的是值时,其实传入的是一份参数的临时拷贝,拷贝就当然需要额外的消耗内存了,如果结构体的占用内存小一点还好,如果内存很大,那么代码的速度就会减慢,而我们传址,传入的是地址,用指针变量来接收,指针变量最多也就是8个字节,这样就极大的节省了空间,但是又有人会问了,如果传入的结构体的指针,我们在函数中可能会修改结构体里面的内容,这样不就显得不安全了吗?没错,这个顾虑是对的,但是我们可以通过适当的修改来解决这个忧患,那就是加上const修饰。

void Print2(const struct S* s)
{printf("%d\n", s->num);
}

说到结构体占用的内存,我们如果想知道结构体的大小该怎么办呢

计算结构体的大小(对齐数)

#includestruct S1
{char c1;int i;char c2;
};int main()
{printf("%d\n",sizeof(struct S1));return 0;
}

结果会是什么呢?6?我们来看看。

12,为什么这个结构体的大小是12呢?

这里我们就需要知道结构体的对齐数了,在结构体中每个地址都有一个跟0地址处的偏移量,每个数据存储的时候都必须存放在该数据类型跟编译器的默认对齐数相比,较小的数字的整数倍,我们平时使用的vs默认的对齐数是8个字节,而gcc则没有默认对齐数。不仅如此,结构体的第一个数据必须得从0偏移处开始存放,在计算出所有的占用内存后,内存的大小还必须是每个成员变量的整数倍数。知道了这些后我们来看看上面这个12是怎么来的吧。 ​​​​​

  

 

 

 

 那么再来一个题,小试一手吧。

#includestruct S1
{char c1;int i;char c2;
};struct S2
{int a;char b;struct S1;
};int main()
{printf("%d\n", sizeof(struct S2));return 0;
}

这个原理跟上面那个一样的,只是一个结构体中多了一个结构体,我们可以把里面的结构体拆开来看。

我们再来了解下面一个知识:位段。

位段 

什么是位段

1.位段的成员得是整形家族

2.位段的成员名后边有一个冒号和一个数字。

例如:

struct A
{int _a:2;int _b:5;int _c:10;int _d:30;
};

A是一个位段类型,那么A的大小是多少呢?

#includestruct A
{char _a : 2;char _b : 5;char _c : 6;char _d : 5;
};int main()
{printf("%d\n", sizeof(struct A));return 0;
}

我们来看看位段在内存中是怎样分配内存的吧。

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

char类型,先分配8个比特位,并且从右向左使用,如果剩下的不够使用,就另外再开辟。 

一共开辟的是3个字节。

 

 枚举

枚举顾名思义就是列举,可以把可能的值都列举出来。

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

这些可能取值都是有值的,默认从0开始,一次递增1,当然在定义的时候也可以赋初值。

枚举的优点

我们可以使用 #define 定义常量,为什么非要使用枚举?
枚举的优点:
1. 增加代码的可读性和可维护性
2. 和#define定义的标识符比较枚举有类型检查,更加严谨。
3. 防止了命名污染(封装)
4. 便于调试
5. 使用方便,一次可以定义多个常量

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

总结

这些就是我学到的关于自定义数据类类型的知识,欢迎大家来点评,记得点赞哦!
 

相关内容

热门资讯

【实验报告】实验一 图像的... 实验目的熟悉Matlab图像运算的基础——矩阵运算;熟悉图像矩阵的显示方法࿰...
MATLAB | 全网最详细网... 一篇超超超长,超超超全面网络图绘制教程,本篇基本能讲清楚所有绘制要点&#...
大模型落地比趋势更重要,NLP... 全球很多人都开始相信,以ChatGPT为代表的大模型,将带来一场NLP领...
Linux学习之端口、网络协议... 端口:设备与外界通讯交流的出口 网络协议:   网络协议是指计算机通信网...
kuernetes 资源对象分... 文章目录1. pod 状态1.1 容器启动错误类型1.2 ImagePullBackOff 错误1....
STM32实战项目-数码管 程序实现功能: 1、上电后,数码管间隔50ms计数; 2、...
TM1638和TM1639差异... TM1638和TM1639差异说明 ✨本文不涉及具体的单片机代码驱动内容,值针对芯...
Qt+MySql开发笔记:Qt... 若该文为原创文章,转载请注明原文出处 本文章博客地址:https://h...
Java内存模型中的happe... 第29讲 | Java内存模型中的happen-before是什么? Java 语言...
《扬帆优配》算力概念股大爆发,... 3月22日,9股封单金额超亿元,工业富联、鸿博股份、鹏鼎控股分别为3.0...
CF1763D Valid B... CF1763D Valid Bitonic Permutations 题目大意 拱形排列࿰...
SQL语法 DDL、DML、D... 文章目录1 SQL通用语法2 SQL分类3 DDL 数据定义语言3.1 数据库操作3.2 表操作3....
文心一言 VS ChatGPT... 3月16号,百度正式发布了『文心一言』,这是国内公司第一次发布类Chat...
CentOS8提高篇5:磁盘分...        首先需要在虚拟机中模拟添加一块新的硬盘设备,然后进行分区、格式化、挂载等...
Linux防火墙——SNAT、... 目录 NAT 一、SNAT策略及作用 1、概述 SNAT应用环境 SNAT原理 SNAT转换前提条...
部署+使用集群的算力跑CPU密... 我先在开头做一个总结,表达我最终要做的事情和最终环境是如何的,然后我会一...
Uploadifive 批量文... Uploadifive 批量文件上传_uploadifive 多个上传按钮_asing1elife的...
C++入门语法基础 文章目录:1. 什么是C++2. 命名空间2.1 域的概念2.2 命名...
2023年全国DAMA-CDG... DAMA认证为数据管理专业人士提供职业目标晋升规划,彰显了职业发展里程碑及发展阶梯定义...
php实现助记词转TRX,ET... TRX助记词转地址网上都是Java,js或其他语言开发的示例,一个简单的...
【分割数据集操作集锦】毕设记录 1. 按要求将CSV文件转成json文件 有时候一些网络模型的源码会有data.json这样的文件里...
Postman接口测试之断言 如果你看文字部分还是不太理解的话,可以看看这个视频,详细介绍postma...
前端学习第三阶段-第4章 jQ... 4-1 jQuery介绍及常用API导读 01-jQuery入门导读 02-JavaScri...
4、linux初级——Linu... 目录 一、用CRT连接开发板 1、安装CRT调试工具 2、连接开发板 3、开机后ctrl+c...
Urban Radiance ... Urban Radiance Fields:城市辐射场 摘要:这项工作的目标是根据扫描...
天干地支(Java) 题目描述 古代中国使用天干地支来记录当前的年份。 天干一共有十个,分别为:...
SpringBoot雪花ID长... Long类型精度丢失 最近项目中使用雪花ID作为主键,雪花ID是19位Long类型数...
对JSP文件的理解 JSP是java程序。(JSP本质还是一个Servlet) JSP是&#...
【03173】2021年4月高... 一、单向填空题1、大量应用软件开发工具,开始于A、20世纪70年代B、20世纪 80年...
LeetCode5.最长回文子... 目录题目链接题目分析解题思路暴力中心向两边拓展搜索 题目链接 链接 题目分析 简单来说࿰...