当前位置: 首页 > news >正文

网站设计动画广告推广免费发布

网站设计动画,广告推广免费发布,什么叫软文,进行网站开发的所有步骤书接上回#xff0c;前面我们已经给大家介绍了如何去声明和创建一个结构体#xff0c;如何初始化结构体变量等这些关于结构体的基础知识。下面我们将继续给大家介绍和结构体有关的知识#xff1a; 今天的主题是#xff1a;结构体大小的计算并简单了解一下位段的相关知识。…书接上回前面我们已经给大家介绍了如何去声明和创建一个结构体如何初始化结构体变量等这些关于结构体的基础知识。下面我们将继续给大家介绍和结构体有关的知识 今天的主题是结构体大小的计算并简单了解一下位段的相关知识。 目录 一、结构体的内存对齐 1发现并提出问题  2内存对齐规则 3解决问题 附一offsetof宏的介绍 4为什么会存在结构体的内存对齐 附二带来的启发 附三修改编译器的默认对齐数 附四关于前面问题的解答 二、关于位段 1搭建应用场景 2位段的概念性理解 3位段的内存分配 学习指南如果你是期末备考的大学生简单学习博主的结构体类型上的内容就够了。如果你对自己有更高的要求希望系统深入了解结构体知识可以继续跟上博主的步伐啦 一、结构体的内存对齐 1发现并提出问题  如果要谈结构体大小的计算就不得不提结构体的底层特性——内存对齐。什么是内存对齐呢我们先来看一下下面这两个例子 #includestdio.h struct S1 {char c1;char c2;int i; };int main() {printf(%zd\n, sizeof(struct S1));return 0; } 这个结构体大小是多少呢是不是 1 1 4(sizeof(char) sizeof(char) sizeof(int))的结果呢我们不妨在自己的机器上运行一下。 下面这张截图是在博主2022 Visual Studio这款IDE上运行的结果 这个结果挺莫名奇妙的我一时也想不清楚这个结果为啥是这个。嗯……我们现在不妨再来做一个事情我们继续声明和创建一个S2的结构体然后呢这个结构体有和S1一样的成员变量c1c2i。然后只是改一下它们之间的顺序我们来看一下这个结构体变量的大小又是多少。如图所示 #includestdio.h struct S1 {char c1;char c2;int i; }; struct S2 {char c1;int i;char c2; };int main() {printf(%zd\n, sizeof(struct S1));printf(%zd\n, sizeof(struct S2));return 0; } 运行截图 这也太不可思议了我仅仅是改了一下结构体各个成员变量的顺序竟然就改变了整个结构体变量的大小。显然种种迹象均表明结构体变量的大小并不是各个成员变量大小的简单相加。 那这具体是怎回事呢欸我们接着往下探索 2内存对齐规则 前面我们通过实验发现结构体变量的大小不是其各个成员变量的简单相加。其实其本质的原因就在于有内存对齐的底层特性存在。 那关于结构体的内存对齐有哪些规则呢这里博主总结了关于内存对齐这一块大致有以下四条规则 第一个成员在与结构体变量偏移量为0的地址处其他成员变量要对齐到某个数字对齐数的整数倍的地址处结构体的总大小为最大对齐数每个成员变量都有一个对齐数的整数倍如果嵌套了结构体嵌套结构体要对齐到自己的最大对齐数的整数倍处其他规则照旧。 上述规则中的一些概念我们这里做简单的阐释。首先关于什么是对齐数 对齐数 Min编译器默认的一个对齐数成员变量自身的大小 一般来说结构体的每一个成员变量都有自己的一个对齐数不同大小的成员变量一般有不同的对齐数其中我们把一个结构体它所有成员变量中最大的那个对齐数称之为最大对齐数。 特别地如果成员变量是一个数组。那这里成员变量的大小注意啦不是指数组的大小而是数组中单个元素的大小。我们下面以VS 2022为例子编译器的默认对齐数是8来说明这个问题 struct S {char c; //8 1 —— 对齐数是1int i; //8 4 —— 对齐数是4char name[12];//8 12 No,No,No! 8 1 ——对齐数是1 }; 另外关于编译器的默认对齐数如果通过查阅相关编译器的技术文档你能够查到该编译器的默认对齐数那当然再好不过了。但是如果说你实在查不到那你就忽略这个编译器的默认对齐数或者说假设它是无穷大。如果说此时发现假设和实际结果不相一致我们通过对结果进行分析也可以推导出编译器的默认对齐数的大小。 那话说回来我们还是希望大家记一些常见的关于编译器的默认对齐数像VS系列都是8然后gcc系列是无穷大无穷大的另一种理解就是对齐数 成员变量自身的大小。 3解决问题 OK当然啦说了这么多我们就可以分析和解决前面所提到的那个问题。我们这里假设是在VS 2022的环境下也就是说编译器的默认对齐数是8。 #includestdio.h struct S1 {char c1;char c2;int i; }; struct S2 {char c1;int i;char c2; };int main() {printf(%zd\n, sizeof(struct S1));printf(%zd\n, sizeof(struct S2));return 0; } 对于结构体struct S1而言它的内存布局即为下图所示 分析 红色块是c1的内存空间旁边的数字对应的是这个内存块相较于结构体起始地址的偏移量。 由于c1是第一个成员因此偏移量为0。而且c1只需要1个字节的空间综上红色块就是c1的内存空间。 绿色块是c2的内存空间这是因为 8编译器默认对齐数 sizeof(c2) 1因此对于c2来说它的对齐数就是1也就是说c2内存的起始位置在偏移量为1的倍数的位置上显然就要我们绿色块的位置上。 蓝色块是i的内存空间这是因为8编译器默认对齐数 sizeof(i) 4因此对于i来说它的对齐数就是4偏移量为4的倍数的位置恰好就是对应我们蓝色块的起始位置。然后往下延展4个字节这是因为sizeof(i) 4。 最后显然最大对齐数就是4此时c1 c2 i的内存大小是8正好就是4的倍数因此结构体的内存大小是8。 对于结构体struct S2而言它的内存布局即为下图所示 分析 c1ic2的分析略这里我们简单说一下结构体的大小分析。对于这个结构体我们不难推导出最大对齐数——4然后此时c1 i c2的内存大小是99不是最大对齐数4的倍数于是向下延展至12个字节此时正好是4的倍数由此结构体变量的大小是12。 如果你现在觉得自己强得可怕不妨试着继续挑战一下下面这两个结构体大小的计算 struct S3 {char c1;int i;char name[12]; }; struct S4 {int i;struct S3 s;char c1; };对于结构体struct S3而言大家只需要注意博主给大家提醒的如果成员变量是一个数组。那对齐数那里所说的成员变量的大小不是指数组的大小而是数组中单个元素的大小。因此最后结构体的最大对齐数也不是12而是4。结构体整体的大小就是20。如图所示 其次对于结构体struct S4而言它里面嵌套了其他的结构体struct S3 s我们只要注意s的对齐数是这个结构体的最大对齐数。就可以了。如果所有的一些没有问题最后struct S4的大小就是28。如图所示 附一offsetof宏的介绍 offsetof是一个宏有了offsetof我们的用户只需要给出一个结构体然后再给出这个结构体的一个成员变量你就可以获得这个成员变量距离结构体起始位置的偏移量。然后如果你想使用这个宏请#includestddef.h 我们这里以前面的struct S4结构体为例子来看一下这个宏具体是如何使用的 #includestdio.h #includestddef.h struct S3 {char c1;int i;char name[12]; }; struct S4 {int i;struct S3 s;char c1; };int main() {printf(struct S4 的i 的相较于起始位置的偏移量%zd\n, offsetof(struct S4, i));printf(struct S4 的s 的相较于起始位置的偏移量%zd\n, offsetof(struct S4, s));printf(struct S4 的c1的相较于起始位置的偏移量%zd\n, offsetof(struct S4, c1));return 0; } 运行截图 而这些和我们所分析的结果是一致的。 有小伙伴可能对这个宏的底层实现比较感兴趣我们这里也把这个offsetof宏的一种实现方式给大家放在下面了 #define offsetof(type, member) ((size_t)((type*)0)-member) 它的基本策略就是把地址为0的位置当作是结构体的起始地址然后结构体的成员member再取地址就是这个结构体成员相较于起始位置的偏移量了。 4为什么会存在结构体的内存对齐 C语言的结构体为什么会有内存对齐的特性。有一种说法是说如果结构体的内存是对齐的话会带来更好的内存访问效率。 OK为了更好的说明这个问题我们先来假定一些场景 假如我们的用户创建了结构体S并定义了一个该结构体的变量s如图所示 struct S {char c;int i; }s; 然后我们假定我们拥有一台32位的机器注意这里的32位是指这台机器的机器字长是32位机器字长32位也就意味着这台机器的CPU一次性要从内存拿32bit的数据然后能一次性对32bit的数据进行处理现代CPU对数据的各种处理都是基于机器字长为单位的。 然后如果说我不是内存对齐的我这次想要处理变量s中的一个成员i那这个过程可以用下图进行阐述 这个图片告诉我们如果说我读的数据是i我们的CPU会从结构体变量s的起始地址处开始读数据如果可以CPU可以选择不这么做显然这里我们不得不这么做一次读4Byte数据4Byte 32bit图中绿框所标识的范围显然我第一次只能拿到i的前3个字节的内容我需要再读一遍才能完整地读取到i。也就是说 如果内存是不对齐的对i变量的读取CPU要读两次。而且对于变量i来说还有一些没必要的东西被读进来了。 紧接着我们继续探讨如果说我们的内存的是对齐的。那这个时候的过程则为下图所示 显然这时我们发现如果说CPU要对i进行处理它完全可以做到只需要读一次就可以将变量i完整地读进来。 从另一个角度当你的数据是对齐的情况下我们内存控制器和CPU的设计呢会更加简单。因为显然的一点如果内存对齐的情况下我们的CPU不需要去处理那种跨边界的数据请求非对齐的情况下进行访问出现跨边界的数据请求时可能需要多次内存操作才能获取到所需要的数据。这增加了我们硬件设计的复杂性并带来了潜在的延迟。 因此我们这里可也以认为是说这种结构体存储成员的策略是一种利用空间效率来换取时间效率的做法。 这是博主能给大家介绍的一种为什么说会存在有内存对齐的一个原因。当然呢也有其他地方有说法是说这种内存对齐的策略会有利于硬件平台的移植……嗯我们不希望在这里把这个问题搞得更复杂了因此这些其他的原因就留给感兴趣的读者朋友去探索一下吧不再进行深入的探讨。 附二带来的启发 在设计结构体时如果我既希望满足对齐又希望所浪费的空间尽可能的少。我们应该做到让占用空间小的成员尽量集中在一起。比如说下面两个结构体的设计显然struct S1的设计优于struct S2的设计 struct S1{char c1;char c2;int i;}; struct S2{char c1;int i;char c2;}; 附三修改编译器的默认对齐数 C语言提供了#pragma pack(对齐数)这种格式的预处理指令来对编译器的默认对齐数进行修改。理论上对齐数都是大于0的正整数特别地如果指令格式里面的对齐数不写或写成0其他的数据类型或负数往往会被认为非法那就表示使用默认对齐数。 我们来看一个例子 #define _CRT_SECURE_NO_WARNINGS 1 #includestdio.h //先将编译器的默认对齐数设置为1 #pragma pack(1) struct S1 {char c;int i; }; //再将编译器的默认对对齐数设置回来 #pragma pack() struct S2 {char c;int i; };int main() {printf(sizeof(struct S1) %zd\n, sizeof(struct S1));printf(sizeof(struct S2) %zd, sizeof(struct S2));return 0; } 运行截图 我们会发现如果编译器的默认对齐数是1带来的感觉就是没有进行内存对齐。当然无论怎么说这应该和我们所预期的结果推理的结果和实际结果一致是一致的。 附四关于前面问题的解答 我们在C语言自定义数据类型详解一——结构体类型上-CSDN博客里面留了一个问题为什么说结构体的传参一般情况下是传址优于传值。现在我们对于一个结构体它的大小是如何计算的应该都已经是小case了回过头来思考这个问题如果我结构体传址的话就如果是指针的话那该参数对于函数栈的消耗要么是4Byte要么是8Bytex86是4x64是8。而一个结构体变量的大小往往会超过8个字节你想一个char一个int然后还有内存对齐这个大小已经就有8Byte了更不要说更复杂的结构体。这相应地所带来的压栈消耗也会更大。 因此我们说结构体传参传址往往优于传值。 二、关于位段 1搭建应用场景 A公司最近购入了四台机器。小明作为该公司的程序员被派遣了一个任务要求建立一个数据结构用于维护四台机器的各自状态这其中就包括了机器各自的id(0123)机器的运行时间time标识机器是否正常运行的状态指针state……这些可能就是作为维护人员我们需要去关心的一些参数 于是小明据此声明了下面这种结构用来维护工厂里的这四台机器 struct Machine {int id; //唯一标识一台机器的idint time; //机器的运行时间int state; //机器目前的状态1表示正常运行0表示不正常 } 但是小明后来在使用过程中也注意到了一个问题在这个结构体中有一些成员的取值是非常有限的比如说一台机器的id只有0123四种可能的结果一个机器的状态也就正常和故障两种。 而对于这样的成员变量我们用int来存嗯……可以是可以但是我实际上能用到的也就2个二进制位而已000011102113。也就是我当前的这种结构id中有30bit都是无效的。 有小伙伴说用char当然啦这也行但是有没有其他的方法呢OK这里跟着博主的步伐来认识一下我们接下来的主角——位段。 2位段的概念性理解 在某些应用场景中为了尽可能地节省空间我们的C语言结构体一般都有能够实现位段的能力。 位段的声明和结构体非常类似但是有两个本质上的区别 位段的成员只能是intunsigned int 或signed int。C99之后支持了更多的数据类型但是基本上就是intchar这样的数据类型。在位段的成员名的后面往往有一个冒号和一个数字用来表示该成员变量的大小。以bit为单位。 也就是说位段的成员虽然冠以int自称但是它的实际大小往往低于int类型的4Byte。我们可以来看一个例子 //我们这里在位段的成员名前面都加了下划线_仅仅是为了便于区分它和一般的结构体成员变量即这仅仅是个命名习惯罢了 struct S {int _a : 2; //_a是位段成员占2 bit位int _b : 5; //_b是位段成员占5 bit位int _c : 10; //_c是位段成员占10 bit位int d; }; 3位段的内存分配 对于位段成员的内存分配C语言也没有提供具体的标准但是一个大致的方向是当你定义了一个位段成员时它会先给你1Byte或4Byte空间这一般取决于你前面的类型写的是int还是char然后你不够了编译器会继续再给你1Byte或4Byte空间。 什么意思呢就拿我们前面定义的结构体来说我们先把它拿过来 #includestdio.h //我们这里在位段的成员名前面都加了下划线_仅仅是为了便于区分它和一般的结构体成员变量即这仅仅是个命名习惯罢了 struct S {int _a : 2; //_a是位段成员占2 bit位int _b : 5; //_b是位段成员占5 bit位int _c : 10; //_c是位段成员占10 bit位int d; };int main() {printf(sizeof(struct S) %zd,sizeof(struct S));return 0; } 首先编译器看你需要一个int类型的成员变量于是它会先给你开辟4Byte空间。显然4Byte 32bit这对于_a_b_c显然是够用了的。因此_a_b_c会公用这4byte的空间。加上之后的int类型的b变量这个结构体最终的大小大概就是8Byte左右。 显然这比你直接用4个int所需要的内存少多了。如图所示 这就是一种简单地分析位段大小的一种方式。这里面其实还有很多的细节如图所示 具体到内部_a要2bit这2bit开辟是从左往右还是从右往左是C语言标准没有规定的。 另外如果说是这样的 #includestdio.h //我们这里在位段的成员名前面都加了下划线_仅仅是为了便于区分它和一般的结构体成员变量即这仅仅是个命名习惯罢了 struct S {int _a : 15; //_a是位段成员占15 bit位int _b : 12; //_b是位段成员占12 bit位int _c : 10; //_c是位段成员占10 bit位 }; 这里我们当然知道说这个结构体是8byte然后_a和_b用了前4Byte的27bit位。问题是来给_c分配空间的时候我们知道前面4Byte用了它还有5bit没有。这5bit会不会给到_c然后再从后面的4Byte拿出5bit凑成最终的5bit呢。 还是说这前面4Byte遗留下的5bit不要了_c的10bit全部在后面的4Byte里面。这件事情也是我们C语言标准所没有规定的事情。 换句话说位段内部在给成员分配空间时它的不确定性因素实在是太多了。这带来的直接问题便是位段的跨平台性非常差因为没有标准每个编译器的厂家都是自己的那一套。因此我们实际中用位段其实用得非常非常少这是希望大家注意到的一点。 博主在这里介绍也仅仅只是希望大家以后在纯C里面看到种定义结构体成员的方式能够看得懂。 至此关于结构体所有相关的周边知识就介绍完啦我们下期再见
http://www.tj-hxxt.cn/news/232309.html

相关文章:

  • 建设银行积分商城网站做微信推送的网站
  • 做网站服务器硬盘多大网站开发的调研内容
  • 自己的网站服务器建设一个电商网站的流程
  • 曲沃网站建设可以大量免费发帖的网站
  • 订做网站建设中山企业网
  • uniapp微信小程序模板黑帽seo优化软件
  • 凡科建站免费家教
  • 郑州网站建设方案服务湖南省新闻最新消息十条
  • 官方网站撰写策划书农业推广
  • 江苏宏远建设集团网站wordpress用户个人页面
  • 网站应该如何推广wordpress 多主题插件下载
  • 青岛网站建设招标@安徽网站建设
  • wordpress建图片网站关于网站推广
  • 网站备案登记表南京seo招聘
  • 未做301重定向的网站电子商务网站建设作业总结
  • 小型企业门户网站制作成都到西安机票
  • 百度推广送的公司网站有什么用php网站后台进不去
  • 网站隐私声明模板学php搞网站开发
  • php直播网站开发通城做网站公司
  • 网站备案帐号是什么意思江西建设厅网站查询施工员
  • 在线制作电子印章软件襄阳百度seo
  • 网站加密软件技术毕业设计题目
  • 廊坊百度提升优化江门seo外包公司
  • 湖北城乡和建设官方网站松江网站开发培训学校
  • 做视频的免费素材网站众创空间文化建设网站
  • 自动跳转到wap网站品质好
  • 网站app怎么做深圳关键词优化
  • 网站如何做vip等级网站建设专家收费标准
  • 开发网站的流程步骤前端开发工程师招聘信息
  • 男男床做第一次视频网站网站开发文案模板