【C语言】新类型,结构体篇-----深入理解结构体,结构体成员的访问,结构体的大小和新概念偏移量!【图文详解】

欢迎来CILMY23的博客喔,本篇为【C语言】新类型,结构体篇-----深入理解结构体,结构体成员的访问,结构体的大小和新概念偏移量!【图文详解】,感谢观看,支持的可以给个一键三连,点赞关注+收藏。

 前言

C语言中的数据类型有内置类型,和用户自定义类型,内置类型包括char ,short,int,double,float,long long……,用户自定义类型包括,结构体,枚举,联合体,数组……本篇博客将会带大家了解用户自定义类型中的结构体。

目录

一、结构体的声明和使用

结构体的声明:

结构体的使用:

特殊的结构体声明: 

二、结构体的访问

 三、结构体大小

结构体嵌套的大小

为什么会有内存对齐?

四、offsetof宏和#pragma预处理指令

 五、结构体的自引用(涉及数据结构)


一、结构体的声明和使用

在过去我们经常使用数组存储同一类型的数据,但我们不仅仅只有同一类型的数据,于是C语言允许用户自己建立由不同类型数据组成的组合型的数据结构,它被称为结构体

结构体的声明:

struct Student
{int num;			//学号为整型char name[20];		//姓名为字符串char sex;		   //性别为字符型int age;		  //年龄为整型float score;      //成绩为浮点型
};

结构体类型的声明是用一个关键字struct +结构体名字,结构体名字是用户指定的。

 

typedef也可以对结构体重命名:

typedef struct Student
{int num;
}Stu;int main()
{Stu n;return 0;
}

结构体的使用:

int main()
{struct Student s = {23,"zhangsan",'m',21,88.9};return 0;
}

这样我们就创建了一个结构体变量,这个变量的信息就如{}中所示。

我们也可以在声明结构体类型的时候声明变量

struct Student
{int num;			//学号为整型char name[20];		//姓名为字符串char sex;		   //性别为字符型int age;		  //年龄为整型float score;      //成绩为浮点型
}s1, s2;

这两种变量还是有差别的,s1,s2是全局变量,s只是局部变量 

 我们创建变量就相当于在内存中开辟了空间,这么多数据类型就构成一个s结构体变量,其中的分布如下:

特殊的结构体声明: 

 我们可以把结构体类型名称省去,这种情况叫做匿名结构体,用匿名结构体类型创建了一个s对象

struct
{int num;			
}s;struct
{int num;			
}*ps;int main()
{ps = &s;return 0;
}

 但是同样要注意,在vs编译器看来这两个匿名结构体每个都是单独的个体,我们并不能把s的地址给到指针ps。

二、结构体的访问

我们有两种访问方式,一种是.,一种是->

 我们把.和->叫做结构成员访问操作符

那如果我们得到的是s 的地址呢,就可以用到->操作符

int main()
{struct Student s = {23,"zhangsan",'m',21,88.9};printf("%d %s\n", s.num, s.name);struct Student* ps = &s;printf("%d %s\n", (*ps).num, (*ps).name);printf("%d %s\n", ps->num, ps->name);return 0;
}

结果如下:

 三、结构体大小

 首先我们来看以下代码:

struct s1
{char c1;char c2;int i1;
};struct s2
{char c1;int i1;char c2;
};struct s3
{double d;char c;int i;
};int main()
{struct s1 st1;struct s2 st2;struct s3 st3;printf("%d\n", sizeof(st1));printf("%d\n", sizeof(st2));printf("%d\n", sizeof(st3));return 0;
}

结果如下:

那为什么会这样呢?这就涉及结构体的大小规则了-----内存对齐 

 内存对齐的规则:

1.结构体的第⼀个成员对齐到相对结构体变量起始位置偏移量为0的地址处
2.其他成员变量要对齐到对齐数的整数倍的地址处。
        对齐数 = 编译器默认的⼀个对齐数与该成员变量大小的较小值。
        VS中默认的值为8
        Linux中没有默认对齐数,对齐数就是成员自身的大小

3.结构体总大小为最大对齐数(结构体中每个成员变量都有⼀个对齐数,所有对齐数中最大的)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体中成员的对齐数)的整数倍。

st1 

偏移量是什么?假设我们有一个内存,偏移量为1个字节就是指向1这个地方了,所以偏移量为0的话就是指从起始位置开始.(可以理解为距离起始位置有多远)

 所以第一个成员c1就存放在第一个字节里

其余成员要按照对齐数来对齐, 根据下面图片来看,c2只需要对齐到1的倍数即可,而i1需要对应到地址字节数4的倍数即可,也就是从第四个字节开始

如下所示: 

结构体总大小为最大对齐数,所有成员最大对齐数是4,st1的大小刚好是4的倍数,所以st1的结构体大小就为8 

 st2

所以我们看st2就容易多了

首先第一个成员要对齐偏移量为0的位置,c1的位置就确定了

其余成员要按照对齐数来对齐, 根据下面图片来看,c2只需要对齐到1的倍数即可,而i1需要对应到地址字节数4的倍数即可,也就是从第四个字节开始,

总的大小为9,结构体总大小为最大对齐数,所有成员最大对齐数是4,所以str2的大小为12.

 st3

首先第一个成员要对齐偏移量为0的位置,d的位置就确定了,其余成员要按照对齐数来对齐, 根据下面图片来看,c只需要对齐到1的倍数即可,而i需要对应到地址字节数4的倍数即可,也就是从第12字节开始。

总的大小为16,结构体总大小为最大对齐数,所有成员最大对齐数是8,所以str2的大小为16.

结构体嵌套的大小

struct S3
{double d;char c;int i;
};struct s4
{char c1; struct S3 s3; double d;
};int main()
{struct s4 st4;printf("%d\n", sizeof(st4));return 0;
}

结果如下:

 st4

如果嵌套了结构体的情况,嵌套的结构体成员对齐到自己的成员中最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数,也就是对齐到s3当中所有最大成员的对齐数,也就是十六的对齐数。

总的大小为16,结构体总大小为最大对齐数,所有成员最大对齐数是16,所以str2的大小为32.

为什么会有内存对齐?

1.    平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
2.    性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要⼀次访问。假设⼀个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用⼀个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。
总体来说:结构体的内存对齐是拿空间来换取时间的做法。 

最后,尽量将字节数小的成员安排在前面,防止空间浪费

四、offsetof宏和#pragma预处理指令

 offsetof宏是用来计算偏移量的,在cplusplus网站可以查询

offsetof - C++ Reference (cplusplus.com)

offsetof的使用如下: 

#include<stdio.h>
#include<stddef.h>struct S3
{double d;char c;int i;
};struct s4
{char c1; struct S3 s3; double d;
};int main()
{printf("%zd\n", offsetof(struct s4, c1));printf("%zd\n", offsetof(struct s4, s3));printf("%zd\n", offsetof(struct s4, d));return 0;
}

结果如下:

 #pragma预处理指令

#pragma pack()//取消设置的默认对⻬数,还原为默认
struct S3
{double d;char c;int i;
};#pragma pack(1)//设置默认对⻬数为1
struct s4
{char c1; struct S3 s3; double d;
};int main()
{printf("%d\n", sizeof(struct s4));return 0;
}

一般使用都会将其设置成2,4,6,8,这样的数值。 

 五、结构体的自引用(涉及数据结构)

可以看以下这篇补充一下链表的结构知识点 ,然后再看结构体的自引用

http://t.csdnimg.cn/BDRBY

假设我们这里有个链表,定义⼀个链表的节点如下所示:

struct Node
{int num;struct Node next;
};int main()
{struct Node n;return 0;
}

 仔细分析,其实是不行的,因为⼀个结构体中再包含⼀个同类型的结构体变量,这样结构体变量的大小就会无穷的大,是不合理的,一个结构体中有一个Node,Node中又有一个Node如此无限循环下去,就会造成结构体变量的大小无穷大了。

正确的自引用应该如下所示:

struct Node
{int data;struct Node* next;
};int main()
{struct Node n;return 0;
}

 同样对tepedef我们仍然要注意以下情况是不能使用的

看以下两个代码对比例子:

typedef struct Node
{int data;Node* next;
}Node;typedef struct
{int data;Node* next;
}Node;

我们得先有结构体类型重命名后,得到的名字,然后再去命名next。 

正确引用如下:

typedef struct Node
{int data;struct Node* next;
}Node;int main()
{Node n = {0};return 0;
}

感谢各位同伴的支持,本期结构体篇就讲解到这啦,如果你觉得写的不错的话,可以给个一键三连,点赞关注+收藏,若有不足,欢迎各位在评论区讨论。  

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【牛客】VL64 时钟切换

描述 题目描述&#xff1a; 存在两个同步的倍频时钟clk0 clk1,已知clk0是clk1的二倍频&#xff0c;现在要设计一个切换电路&#xff0c;sel选择时候进行切换&#xff0c;要求没有毛刺。 信号示意图&#xff1a; 波形示意图&#xff1a; 输入描述&#xff1a; clk0 clk1为时…

超详细的Scrapy框架的基本使用教程

Scrapy的介绍 scrapy的工作流程&#xff08;重点&#xff01;&#xff01;&#xff01;&#xff09; 如下图所示&#xff1a; 爬虫&#xff1a; 负责向引擎提供要爬取网页的URL&#xff0c;引擎会把这个URL封装成request对象并传递给调度器&#xff0c;把引擎传递过来的resp…

冒泡经典题

&#x1f4d1;前言 本文主要是【】——简单使用的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 &#x1f304;每日一句&#xff1a;狠…

搜维尔科技:捕获、分析、优化,使用 Xsens Ergo 创建更安全的工作空间

简化人体工程学分析&#xff0c;优先考虑员工福祉&#xff0c;并利用客观数据和见解提高生产力。 捕获。分析。优化。使用 Xsens Ergo 创建更安全的工作空间 1.质量数据 使用高质量、客观且经过验证的运动数据进行详细的人体工程学分析 2.随处使用 在最具挑战性的工作环境中…

C++面试干货---带你梳理常考的面试题(二)

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 1.struct 和 class 区别 1.默认访问权限&#xff1a;struct中的成员默认为public&#xff0c;而class中的成员默认为priv…

【知识梳理】关于网络分层

转载说明&#xff1a;如果您喜欢这篇文章并打算转载它&#xff0c;请私信作者取得授权。感谢您喜爱本文&#xff0c;请文明转载&#xff0c;谢谢。 网络协议分层 网络协议通常分不同层次进行开发&#xff0c;每一层分别负责不同的通信功能。一个协议族&#xff0c;比如 TCP/IP…

JDK17在Windows安装以及环境变量配置(超详细的教程)

目录 一、JDK17的安装包下载 二、安装JDK17 第一步&#xff1a;运行JDK的EXE文件 第二步&#xff1a;选择下一步 第三步&#xff1a;选择安装目录 第四步&#xff1a;安装完成 三、配置JDK17的环境变量 第一步&#xff1a;打开系统属性界面 第二步&#xff1a;打开高级…

Jekins 自启动Java应用的Shell笔记

背景 最近在研究jdk 的jvisualvm 对JVM服务远程监控时&#xff0c;意外的与jekins接轨了。公司使用jekins自动从Git上获得源码&#xff0c;打包后传到测试服务器并启动jar包&#xff0c;实现自动部署&#xff0c;而我需要做的是在测试服务器启动jar包时添加几个我设置的命令&am…

LeetCode刷题日志-17.电话号码的字母组合

纯暴力解法&#xff0c;digits有多长&#xff0c;就循环多少次进行字母组合 class Solution {public List<String> letterCombinations(String digits) {List<String> reslut new ArrayList<>();if(digits.equals(""))return reslut;Map<Inte…

【一起学习Arcade】(6):属性规则实例_约束规则和验证规则

一、约束规则 约束规则用于指定要素上允许的属性配置和一般关系。 与计算规则不同&#xff0c;约束规则不用于填充属性&#xff0c;而是用于确保要素满足特定条件。 简单理解&#xff0c;约束规则就是约束你的编辑操作在什么情况下可执行。 如果出现不符合规则的操作&#…

三八妇女节送礼推荐:送给她的五款超值好物,绝不踩雷!

随着三八妇女节的临近&#xff0c;我们开始思考如何向身边的女性表达我们的尊重和关爱。这个特殊的节日不仅是对女性贡献的认可&#xff0c;更是展示我们关怀与感激之情的绝佳时机。在众多礼物中&#xff0c;如何挑选一份既能体现心意又实用的礼品呢&#xff1f;为了让您在这个…

MyBatis介绍

MyBatis是一个优秀的持久层框架&#xff08;就是将某些数据持久化到硬盘或其他存储器中的框架&#xff09;&#xff0c;它把jdbc对数据库的操作进行了封装&#xff0c;使用户只需关注sql本身&#xff0c;不需要去执行jdbc的那一套复杂的操作。 MyBatis通过配置xml文件或注解的方…