详解结构体(包含结构体内存对齐,柔性数组,位段)【尊嘟很详细】

结构体

结构体是一些值的集合,这些值称为成员变量,结构的成员可以是标量、数组、指针,甚至是其他结构体。

成员名可以与程序中其它变量同名,互不干扰。

结构体的定义

(struct+结构名+{})

struct books
{int a;char b;struct book* c;
};//分号不能少//

结构体成员不能在结构体内赋初值

关键字struct与结构名一起构成结构类型名

struct books是一个结构类型名

结构体可以嵌套定义

但结构体定义时***不允许***将成员的数据类型定义成自身的结构类型,这是因为结构类型的声明是构造阶段,系统还不知道需要分配多少内存空间。
但是结构类型中可以含有指向自身类型的指针变量。

结构变量

定义:

①在结构体定义时定义,此时变量位于结构体{}之后的;之前

在这里插入图片描述
②在结构体定义完后定义

在这里插入图片描述

③在匿名结构体定义时定义

在这里插入图片描述
由于此定义***省去了结构名***,在此定义语句后面无法再定义这个类型的其他结构变量,除非把定义过程再写一遍。

另外,以后如果再声明成员完全相同的结构类型,也和此次定义的结构类型属于不同的结构类型。

初始化:

对结构变量初始化时,需要按照其成员出现的顺序对每个成员依次赋值.
不能跳过前面的成员给后面的成员赋值

例如下面的用法是错误的:

struct books ps = { 1002,  ,p};

运算:

相同类型的结构体变量可以进行整体赋值,但***不能***进行关系运算

传参:

①传值:要重新拷贝一份结构体变量,空间和时间的浪费比较大。

②传址:只需要传4/8个字节,速度更快。

所以结构体传参的时候最好使用传址调用

用typedef给结构体命名

结构体的类型名是 struct+结构名,如果觉得它太长了,可以在定义时/定义完成后用typedef给结构体重命名。

重命名方法:

①非匿名结构体重命名

在这里插入图片描述

②匿名结构体重命名

在这里插入图片描述

结构体重命名之后就可以像定义int 类型变量一样,定义结构变量了

在这里插入图片描述
也可以在定义后给结构体重命名(此时只能给非匿名结构体重命名
给匿名结构体重命名会报错)
在这里插入图片描述

结构体的内存对齐(用干计算结构体的大小):

偏移量

是结构体变量的起始地址,向地址大的的增加量,如下图的柱形图的右侧的0.1.2.3.4.5.6.7.8.9等就是偏移量的值

在这里插入图片描述

结构体的对齐规则:

①第一个成员的首个字节在与结构体变量偏移量为0的地址处。

②其他成员变量要对齐到某个数字(对齐数)的整数倍的偏移量处。

对齐数=编译器默认对齐数与该成员自身字节中的***二者的较小值***。

有默认对齐数的编译器很少,常用的只有VS有默认对齐数,其值为8

默认对齐数也可以修改:

#pragma pack(数字)可以修改默认对齐数为括号中的数字

再次写#pragma pack()可以恢复成原默认对齐数

③结构体总大小为最大对齐数(每个成员变量都有一个对齐数,其中成员对齐数最大的成员的对齐数就是该结构体的最大对齐数)的整数倍。

④如果嵌套了结构体的情况,嵌套的结构体的第一个成员对齐嵌套结构体的最大偏移量的整数倍处
最外层结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍

④如果成员是数组,则该成员的对齐数为它这个数组中元素类型的***字节数与编译器默认对齐数中较小的那一个。***

来算几个结构体的大小吧

struct books
{int a;char b;int c;
}sz;

a为第一个成员,所以它的第一个字节空间占据0偏移量,因为int类型占4个字节,所以还有3个字节的空间占据了1~3偏移量

因为除了第一个成员以外其他成员变量要对齐到其对齐数的整数倍的偏移量处
b的对齐数为1,因为4是1的倍数所以,char占据4偏移量处

c的对齐数为编译器默认对齐数与该成员自身字节中的二者的较小值,所以为4,
因为5~7不是4的整数倍,所以被舍弃,c的第一个字节空间占据第8个偏移量处,
剩下3个字节空间占据9~11偏移量。

该结构体的最大对齐数为4,0~11偏移量正好12个字节,因为12是4的整数倍,所以该结构体大小为12;

如图(啊,我图画的好丑):
在这里插入图片描述

struct books2
{char d;char e;double f;
};struct books1
{int a;char b;struct books2 c;
};

a的对齐数为4,占据0-3偏移量,
b的对齐数为1,占据4偏移量
因为***嵌套的结构体的第一个成员对齐嵌套结构体的最大偏移量的整数倍处***
所以·先计算struct bools2的最大对齐数,为f的对齐数,值为8;
所以5-7偏移量舍去,
d占据8偏移量
e占据9偏移量
10-15没有8的倍数,所以舍弃
所以f占据16-23偏移量
结构体struct books1 的最大偏移量为struct books2中的f为8,因为0-23偏移量的字节数为24,为8的整数倍,所以struct books1的大小为24个字节

位段:

位段的定义:

C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”( bit field) 。
利用位段能够用较少的位数存储数据。

在这里插入图片描述

位段的特点:

①位段的成员可以是int ,unsigned int, signed int ,long(只要是整型就可以,浮点数不行)或者是char (属于整形家族)类型

②位段的 :后的值是 :前的位段成员所占的比特位(注意:位段中的变量所占比特位不能大于自身类型字节,例int类型的位段成员所占比特位不能超过32)

③位段的空间上是按照需要(根据类型)以4个字节(int) 或者1个字节( char )或者8个字节的方式来开辟的。不够用再按照需要(根据类型)以4个字节(int) 或者1个字节( char )开辟

struct books
{int _a:23;char _b:4;long _c: 22;
}A;

第一次遇到的类型为int所以开辟32个字节,这32个字节就可以囊括a,b了,因为c占22个字节而32-23-4=5<22,所以要再申请字节,因为遇到的是long所以再申请32个字节。

④位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

⑤位段只能存在于结构体中

位段中的截断

位段的变量赋值时

若位段的比特位小于赋值给位段的值的比特位时,会发生截断。

例:

struct books
{int _a:2;char _b:4;long _c: 2;
}A;
int main()
{A._c = 5;printf("%d \n", A._c);return 0;
}

_c的比特位为2,但是5的二进制位为101,所以去掉最高位1,存入了01,打印出来的就是1.
在这里插入图片描述

位段的跨平台问题:

1.类型为int位段成员(不写unsigned int/signed int)时被当成有符号数还是无符号数是不确定的。

2.位段中最大位的数目不能确定。(例如:long在Windows64位环境下占32个比特位,在Linux的64位环境下long却占64个比特位)

3.位段中的成员在内存中从左向右分配,还是从右向左分配(即存放数据时从左边的比特位开始存放,还是从右边的比特位开始存放)的c语言标准尚未定义。

4.当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时
例上面提到的:

struct books
{int _a:23;char _b:4;long _c: 22;
}A;

第一次遇到的类型为int所以开辟32个字节,这32个字节就可以囊括a,b了,因为c占22个字节而32-23-4=5<22,所以要再申请字节,因为遇到的是long所以再申请32个字节。
就有一个问题,32-23-4剩下的5个比特位是给c还是舍弃,这个问题的答案是不确定的[没有国际标准],所以不同平台可能不同。

位段总结:

跟结构体相比,位段可以达到同样的效果,是可以很好的节省空间,但是有跨平台的问题存在。

柔性数组:

定义:

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

特点:

①柔性数组只能存在于结构体中,且必须是结构体的最后一个成员

②结构中的柔性数组成员前面必须至少一个其他成员

③sizeof 返回的这种结构体大小不包括柔性数组的内存。

④包含柔性数组成员的结构体要用malloc ()函数进行内存的动态分配,并且分配的内存应该大于结构的大小,以适应柔性数组的预期大小(即要给柔性数组前的成员动态内存分配)。

柔性数组的使用:

定义一个指针让其接受malloc的返回值,malloc的字节数为柔性数组前的成员的字节总数+给柔性数组的字节数

在这里插入图片描述
这样开辟的空间,也可以用realloc调整

有人就问了,为什么不能让struct books中的b设置成一个指针,然后让b动态内存申请呢?
其实是可以的,但是如果要保证***结构体中的所有成员都在堆区***,就要malloc两次
如下图:
在这里插入图片描述

柔性数组的好处

第一个好处是:方便内存释放

如果我们的代码是在一个给别人用的函数中,你在里面做了二次内存分配,并把整个结构体返回给用户。用户调用free可以释放结构体,但是用户并不知道这个结构体内的成员也需要free,所以你不能指望用户来发现这个事。
所以,如果我们把结构体的内存以及其成员要的内存一次性分配好了,并返回给用户一个结构体指针,用户做一次free就可以把所有的内存也给释放掉。

第二个好处是:这样有利于访问速度.

连续的内存有益于提高访问速度,也有益于减少内存碎片。(其实, 我个人觉得也没多高了,反正你还是要用做偏移量的加法来寻址)

总结

柔性数组知道的人少不是没道理的,因为它确实没多大用,就算知道柔性数组的人也很少用它,

以上就是全部内容了,如果对你有帮助就点个赞支持一下吧!

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

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

相关文章

饮用水除溴酸盐和硝酸盐中的应用与优势

随着人们对健康和生活质量的日益关注&#xff0c;饮用水安全问题成为了社会关注的焦点。在自然水体中&#xff0c;溴酸盐和硝酸盐的含量往往较高&#xff0c;而这些物质对人体健康存在一定的潜在风险。因此&#xff0c;饮用水处理中如何有效去除溴酸盐和硝酸盐&#xff0c;成为…

TypeScript下载安装,编译运行

TypeScript是拥有类型的JavaScript超集&#xff0c;它可以编译成普通、干净、完整的JavaScript代码。 简单理解&#xff1a;TypeScript就是加强版的JavaScript。 TypeScript最终会被编译成JavaScript代码&#xff0c;那么我们必然需要对应的编译环境 环境搭建前提&#xff1a…

【扩散模型】7、GLIDE | 文本指引的图像生成和编辑

论文&#xff1a;GLIDE: Towards Photorealistic Image Generation and Editing with Text-Guided Diffusion Models 代码&#xff1a;https://link.zhihu.com/?targethttps%3A//github.com/openai/glide-text2im 出处&#xff1a;OpenAI 一、背景 在扩散模型经过了一系列…

【51单片机系列】DS1302时钟模块

本文是关于DS1302时钟芯片的相关介绍。 文章目录 一、 DS1302时钟芯片介绍二、DS1302的使用2.1、DS1302的控制寄存器2.2、DS1302的日历/时钟寄存器2.3、片内RAM2.4、DS1302的读写时序 三、SPI总线介绍四、DS1302使用示例 一、 DS1302时钟芯片介绍 DS1302是DALLAS公司推出的涓流…

GitLab 删除或移动项目

首先明说&#xff0c;删除后无法恢复 第一步&#xff1a;找到要删除的项目 第二步&#xff1a;进入目录后&#xff0c;左侧菜单&#xff0c;设置 >>> 通用&#xff0c;拉到最下面找到“高级”&#xff0c;点击右侧“展开” 第三步&#xff1a;点击“展开”后往下拉&a…

CSS 文字弹跳效果

鼠标移过去 会加快速度 <template><div class"bounce"><p class"text" :style"{animationDuration: animationDuration}">欢迎使用UniApp Vue3&#xff01;</p></div> </template><script> export d…

ArcGIS渔网的多种用法

在ArcGIS中有一个渔网工具&#xff0c;顾名思义&#xff0c;可以用来创建包含由矩形像元所组成网络的要素类。不太起眼&#xff0c;但它的用途却有很多&#xff0c;今天跟大家分享一篇关于渔网的多种用途。 1.马赛克地图制作 2.基于网格的设施密度统计制作马赛克地图 准备材…

牙齿敏感困扰?试试清九野小红盾牙膏

随着冬季的来临&#xff0c;许多人可能会面临牙齿敏感的问题。在这个时候&#xff0c;食用冷饮都会让牙齿感到非常酸痛。一般来说&#xff0c;不良的饮食习惯和不正确的刷牙方式&#xff0c;都会导致牙齿敏感&#xff0c;但根本还是牙本质小管的暴露和空洞&#xff0c;需要修复…

家校互通小程序实战开发02首页搭建

目录 1 创建应用2 搭建首页总结 我们上一篇介绍了家校互通小程序的需求&#xff0c;创建了对应的数据源。有了这个基础的分析之后&#xff0c;我们就可以进入到开发阶段了。开发小程序&#xff0c;先需要创建应用。 1 创建应用 登录控制台&#xff0c;点击创建应用&#xff0c…

File Inclusion(Pikachu)

File Inclusion(local) 这里随便点击一个提交 观察url&#xff0c;显示是一个文件file1.php 可以直接通过url修改这个文件 找到自己的文件&#xff08;本地文件&#xff09;shell.php的路径写上去 就可以看到 File Inclusion&#xff08;remote&#xff09; 提交的是一个目标…

C#教程(四):多态

1、介绍 1.1 什么是多态 在C#中&#xff0c;多态性&#xff08;Polymorphism&#xff09;是面向对象编程中的一个重要概念&#xff0c;它允许不同类的对象对同一消息做出响应&#xff0c;即同一个方法可以在不同的对象上产生不同的行为。C#中的多态性可以通过以下几种方式实现…

促进家校沟通的方法有哪些

“家校沟通是教育中的重要一环&#xff0c;它可以帮助教师和家长更好地了解和关心孩子&#xff0c;共同促进孩子的健康成长。但是&#xff0c;在实际操作中&#xff0c;如何才能有效地促进家校沟通呢&#xff1f; 定期家长会&#xff1a;每个学期开始和结束时&#xff0c;以及期…

【Unity6.0+AI】Sentis加载模型识别手写数字案例实现

按照国际惯例,看效果: 素材准备: 自己在PS中绘制黑底白字手写字体,导出jpg,尺寸28*28! 素材设置 基本步骤 准备工作:从 ONNX Model Zoo 下载手写识别 ONNX 模型文件 【下载模型】MNIST 手写数字识别模型 mnist-12.onnx,并将其拖入项目窗口的 Assets 文件夹。 【下载模…

每日一题-----逆序字符串

大家好我是Beilef&#xff0c;在一个美好的下午我意外接触到编程并且产生了兴趣&#xff0c;哈哈我要努力成为一个跨界者&#xff0c;让我们一起加油吧O(∩_∩)O 文章目录 目录 文章目录 前言 大家好请上车 一、逆序字符串 题⽬描述&#xff1a; 输⼊⼀个字符串&#xff0c;写…

7.7、kali linux环境下搭建DVWA

目录 一、资料下载准备工作 1.1、DVWA源代码下载 二、开启Apache、mysql服务 2.1、下载Apache2文件 2.2、开启Apache2服务 方法一&#xff1a;开启Apache2服务&#xff08;手动&#xff09; 方法二&#xff1a;开启Apache2服务&#xff08;系统自启动&#xff09; 2.3、…

量子密码学简介

量子密码学&#xff08;英语&#xff1a;Quantum cryptography&#xff09;泛指利用量子力学的特性来加密的科学。量子密码学最著名的例子是量子密钥分发&#xff0c;而量子密钥分发提供了通信两方安全传递密钥的方法&#xff0c;且该方法的安全性可被信息论所证明。目前所使用…

blackbox黑盒监控部署(k8s内)tensuns专用

一、前言 部署在k8s中需要用到deployment、configmap、service服务 二、部署 创建存放yaml的目录 mkdir /opt/blackbox-exporter && cd /opt/blackbox-exporter 编辑blackbox配置文件&#xff0c;使用configmap挂在这 vi configmap.yaml apiVersion: v1 kind: Confi…

C#与VisionPro联合编程

C#与VisionPro联合 1. 参照康耐视提供的样例2. 参照样例写一个1. 创建工程2. 添加引用3. 声明变量4. 初始化5. 刷新队列6. 用户数据获取7. 跨线程访问Windows控件--委托8. 显示图像9. 释放资源 3. 代码4. 资源下载 1. 参照康耐视提供的样例 C:\Program Files\Cognex\VisionPro…

详解Java多线程、线程池及线程同步(synchronized关键字、悲观锁、乐观锁)——通俗易懂版!!!

1.进程与线程定义 进程包含线程&#xff0c;如一个百度网盘进程&#xff0c;该进程的线程可以有上传&#xff0c;下载。 2.创建线程的三种方式 方式1-继承Thread类 方式2-实现Runnabled接口 1.常规写法 2.匿名内部类写法 方式3-实现Callable接口 示例代码&#xff1a; f1.get…

CAD objectArx 在操作mfc时出现“不支持尝试执行的操作“

问题原因&#xff1a; ARX中对话框通常继承自CAcUiDialog&#xff0c;CAcUiDialog 构造函数有个参数 HINSTANCE hInstance&#xff0c;默认为 NULL&#xff0c;指定了对话框资源所在DLL进程。如果没有指定该参数&#xff0c;在创建对话框&#xff08;DoModal或Create&#xff…