【详解】结构体的内存对齐(每步配图)

目录

引言:

为什么存在结构体内存对齐?

结构体内存对齐规则:

练习一:

测试代码:

结果如下:

第二个练习:结构体的嵌套问题

测试代码:

代码结果如下:

两个关于结构体的易错点:

第一个易错点:

第二个易错点:结构的⾃引⽤

解决方法:

结语:


引言:

今天我们就要来学习一下考试经常考察的结构体内存对齐问题,相信有很多同学都被这个问题恶心过🤢,不过没有关系今天我会给你们讲清楚的,请各位系好安全带,我们要开始出发啦😃

为什么存在结构体内存对齐?

大部分的参考资料都是这样说的:

1. 平台原因 (移植原因):

不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

2. 性能原因:

数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要⼀次访问。假设一个处理器总是从内存中取8个字节,则地址必须是8的倍数。如果我们能保证将所有的double类型的数据的地址都对齐成8的倍数,那么就可以用一个内存操作来读或者写值了。否则,我们可能需要执行两次内存访问,因为对象可能被分放在两个8字节内存块中。

总体来说:结构体的内存对齐是拿空间来换取时间的做法。 

介绍完原因后,接下来是结构体内存对齐的规则(重中之重,这里一定要弄懂啊!!!)

结构体内存对齐规则:

1.结构体的第⼀个成员对⻬到和结构体变量起始位置偏移量为0的地址处

2. 其他成员变量要对⻬到某个数字(对⻬数)的整数倍(0,1,2...)的地址处。

对⻬数=编译器默认的⼀个对⻬数与该成员变量⼤⼩的较⼩值。

- VS 中默认的值为 8

- Linux中 gcc 没有默认对⻬数,对⻬数就是成员⾃⾝的⼤⼩

3. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。

4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。 

接下来为了帮助大家理解下面我给出几个实例大家可以自己手算

练习一:

因为c1是char类型1个字符,系统给的对齐数为8,和1相比取小的,c1的对齐数为min(1,8)所以在哪开始都可以,i是int类型4个字节要从它的整数倍开始,所以只能从min(4,8)开始,c2在哪都可以开始,最后不要忘了第三点结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对⻬数中最⼤的)的整数倍。这里特别注意是总大小,不是最大下标,是最大下标加一。故最后下标到11位置,图解如下:

c1用蓝色表示,浪费的地址为白色,i为红色,c2为绿色

测试代码:

#define  _CRT_SECURE_NO_WARNINGS 1
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
struct S1
{char c1;int i;char c2;
};
int main()
{int t = sizeof(struct S1);printf("%d", t);
}

结果如下:

第二个练习:结构体的嵌套问题

解决这个问题后,大家结构体内存对齐问题就没有问题啦,大家快快算算看🙌

//练习2-结构体嵌套问题
struct S3
{
    double d;
    char c;
    int i;
};
struct S4
{
    char c1;
    struct S3 s3;
    double d;
};
printf("%d\n", sizeof(struct S4));

解释如下:c1跳过,既然要计算s3,那么我们就要返回S3中,先算s3的内存占多大,d为double类型8个字节min(8,8),0是8的整数倍,c随便,i对齐数为min(4,8),9不是4的倍数,故跳到12,最后算下来s3占的字节数为16图解如下。

计算完s3,我们终于可以计算s4了,s3的填入看看我们对齐规则的第四点:4. 如果嵌套了结构体的情况,嵌套的结构体成员对⻬到⾃⼰的成员中最⼤对⻬数的整数倍处,结构体的整体⼤⼩就是所有最⼤对⻬数(含嵌套结构体中成员的对⻬数)的整数倍。在本题结构体成员的成员中最大对齐数为d的8,故s3的对齐数为8,d为min(8,8),最后别忘了,第三点:3. 结构体总⼤⼩为最⼤对⻬数(结构体中每个成员变量都有⼀个对⻬数,所有对齐数中最⼤的)的整数倍。

总图解如下:(c1蓝色,s3绿色,d红色,白色为浪费空间)

测试代码:

#define  _CRT_SECURE_NO_WARNINGS 1
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
//练习4-结构体嵌套问题
struct S3
{double d;char c;int i;
};
struct S4
{char c1;struct S3 s3;double d;
};
int main()
{printf("%d\n", sizeof(struct S4));return 0;
}

代码结果如下:

补充一个小知识:编译器的默认对齐数是可以自己修改的,c语言就是这么霸道

#pragma这个预处理指令,可以修改编译器的默认对齐数。用法如下:要加一个pack()

#define  _CRT_SECURE_NO_WARNINGS 1
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdio.h>
#pragma pack(1)//设置默认对⻬数为1
struct S
{char c1;int i;char c2;
};
#pragma pack()//取消设置的对⻬数,还原为默认
int main()
{//输出的结果是什么?printf("%d\n", sizeof(struct S));return 0;
}

到这我就已经把结构体的对齐给你讲清楚了(大家一定要自己算算),考试遇到直接给它喵了!!!

两个关于结构体的易错点:

关于结构体为了防止大家出错,这里我给大家总结出来两个易错点供大家参考,大家看看自己有没有遇到这些情况,遇到了又该如何解决呢?下面我将带大家避免掉入这些坑。

第一个易错点:

大家先看看下面这个代码合不合法

struct
{int a;char b;float c;
}x;
struct
{int a;char b;float c;
}a[20], * p;
//下面这个合法吗
*p = x;

我们可以看到这是因为没有给结构体命名导致的争议,其实这是非法的,因为这两个结构体是不同的🙅‍,也就是说这两个结构体不是同一个类型的,所以匿名的结构体类型,如果没有对结构体类型重命名的话,基本上只能使用一次。

第二个易错点:结构的⾃引⽤

下面这个代码合法吗?🐱‍🏍

如果正确的话那么sizeof(struct Node)的值是多少?

很明显这是非法的,因为这样sizeof(struct Node)的值是无穷的,就好像递归没有出口一样

正确引用如下:

我们可以看到这就是类似链表洛

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

解决方法:

定义结构体不要使用匿名结构体了(好习惯要记住哟!!!)

结语:

其实写博客不仅仅是为了教大家,同时这也有利于我巩固自己的知识点,和一个学习的总结,由于作者水平有限,对文章有任何问题的还请指出,接受大家的批评,让我改进,如果大家有所收获的话还请不要吝啬你们的点赞收藏和关注,这可以激励我写出更加优秀的文章。

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

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

相关文章

WPF实现右键选定TreeViewItem

在WPF中&#xff0c;TreeView默认情况是不支持右键选定的&#xff0c;也就是说&#xff0c;当右键点击某节点时&#xff0c;是无法选中该节点的。当我们想在TreeViewItem中实现右键菜单时&#xff0c;往往希望在弹出菜单的同时选中该节点&#xff0c;以使得菜单针对选中的节点生…

Day28 17电话号码的字母组合 39组合求和 40组合求和II

17 电话号码的字母组合 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 因为输入的数字的数量是不确定的&#xff0c;所以for循环的次数也是不确定的&…

2024最新外卖CPS分销微信小程序源码【前端+后台+数据库+分销功能】

内容目录 一、详细介绍二、效果展示三、源代码下载地址 一、详细介绍 外卖侠CPS全套源码是一款为外卖平台提供分销功能的微信小程序。用户可以通过你的链接去领取外卖红包&#xff0c;然后去下单点外卖&#xff0c;既能省钱&#xff0c;又能获得佣金。该小程序带有商城、影票、…

制作 Kali 可启动 USB 驱动器

Kali USB驱动器&#xff0c;轻松安全&#xff0c;获取最新镜像&#xff0c;开始强大的安全测试&#xff01; Kali 可启动 USB 驱动器的优点&#xff1a; 不会更改主机系统的硬盘驱动器或已安装的操作系统&#xff0c;并且要返回正常操作&#xff0c;您只需删除“Kali Live”U…

【Docker篇】使用Docker操作镜像

文章目录 &#x1f6f8;镜像&#x1f33a;基本操作⭐docker --help⭐docker pull [ 参数 ]⭐docker images⭐docker save -- 导出⭐docker rmi -- 删除⭐docker load -- 导入 &#x1f6f8;镜像 镜像是指在计算机领域中&#xff0c;通过复制和创建一个与原始对象相似的副本的过…

【并发编程篇】详解Forkjoin

文章目录 &#x1f354;什么是Forkjoin&#x1f388;Forkjoin的方法&#x1f386;代码实现 &#x1f354;什么是Forkjoin Fork/Join 是一种在多线程领域中常用的算法或技术&#xff0c;它的核心思想是将大任务分割成若干个小任务&#xff0c;然后将这些小任务分配给多个线程并…

程序设计语言的基本成分

程序设计语言的基本成分 1、程序设计语言的数据成分2、程序设计语言的运算成分3、程序设计语言的控制成分4、程序设计语言的传输成分5、函数 程序设计语言的基本成分包括数据、运算、控制和传输等。 1、程序设计语言的数据成分 程序设计语言的数据成分指一种程序设计语言的数据…

如何用ChatGPT写教案设计?以“沁园春雪”为例

1. 引言 随着人工智能技术的飞速发展&#xff0c;ChatGPT已成为教育领域的一大创新工具。ChatGPT不仅能够模拟人类对话&#xff0c;还可以帮助设计互动丰富、内容丰富的教案。本文将探索如何利用ChatGPT进行教案教学设计&#xff0c;特别是通过“沁园春雪”这一案例&#xff0…

【数据结构】——期末复习题题库(9)

&#x1f383;个人专栏&#xff1a; &#x1f42c; 算法设计与分析&#xff1a;算法设计与分析_IT闫的博客-CSDN博客 &#x1f433;Java基础&#xff1a;Java基础_IT闫的博客-CSDN博客 &#x1f40b;c语言&#xff1a;c语言_IT闫的博客-CSDN博客 &#x1f41f;MySQL&#xff1a…

Flutter之配置环境创建第一个项目

随着时代发展&#xff0c;使用Flutter开发的项目越来越多&#xff0c;于是踏上了Flutter开发之路。 作为一个Android开发人员&#xff0c;也只能被卷到与时俱进&#xff0c;下面一起创建一个Flutter项目吧。 一、Android开发&#xff0c;电脑上已经具备了的条件&#xff1a; …

【网络安全】【密码学】【北京航空航天大学】实验四、古典密码(上)【C语言实现】

实验四、古典密码&#xff08;上&#xff09; 一、实验目的 1、 通过本次实验&#xff0c;了解古典加密算法的主要思想&#xff0c;掌握常见的古典密码。 2、 学会应用古典密码&#xff0c;掌握针对部分古典密码的破译方法。 二、原理简介 古典密码的编码方法主要有两种&am…

“蛰伏”的科大讯飞

作者 | 曾响铃 文 | 响铃说 有关科大讯飞的消息&#xff0c;在2024的开端&#xff0c;突然变得多了起来。 先是王牌业务之一的医疗版块即将拆分赴港上市&#xff0c;然后又爆出“拿下大单”&#xff0c;与中国绿化合作签约&#xff0c;要成立合资公司共同组建人工智能产业发…