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

news2024/9/28 13:29:00

目录

引言:

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

结构体内存对齐规则:

练习一:

测试代码:

结果如下:

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

测试代码:

代码结果如下:

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

第一个易错点:

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

解决方法:

结语:


引言:

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

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

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

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.coloradmin.cn/o/1379480.html

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

相关文章

thinkphp递归实现无限级子分类合并上级children

//设别分类列表public function getCategoryList(){$list = Db::name(categorys)->select(

在vue中实现树形结构的表格,以及对数据结构的处理

需求&#xff1a;有一些告警数据&#xff0c;如果他们的计划编码相同则实现折叠效果&#xff0c;单击某行数据可以进行关闭&#xff0c;状态发生改变&#xff0c;关闭以后按钮禁用。 实现效果&#xff1a;目前所有告警消息都被关闭&#xff0c;如果未被关闭则可以进行关闭 实现…

leetcode 2645. 构造有效字符串的最少插入数-python

题目&#xff1a; 给你一个字符串 word &#xff0c;你可以向其中任何位置插入 “a”、“b” 或 “c” 任意次&#xff0c;返回使 word 有效 需要插入的最少字母数。 如果字符串可以由 “abc” 串联多次得到&#xff0c;则认为该字符串 有效 。 解题方法 1.先判断字符串是否…

李沐之神经网络基础

目录 1.模型构造 1.1层和块 1.2自定义块 1.3顺序块 1.4在前向传播函数中执行代码 2.参数管理 2.1参数访问 2.2参数初始化 3.自定义层 3.1不带参数的层 3.2带参数的层 4.读写文件 4.1加载和保存张量 4.2加载和保存模型参数 1.模型构造 1.1层和块 import torch fr…

一种具有轨迹优化的无人驾驶车实时运动规划器 论文阅读

论文题目&#xff1a;A Real-Time Motion Planner with Trajectory Optimization for Autonomous Vehicles Abstract 本文的实时规划器首先将空间离散化&#xff0c;然后基于一组成本函数搜索出最佳轨迹。迭代优化所得到的轨迹的Path和Speed。post-optimization计算复杂度低&…

阿里云服务部署docker容器

1.1 为什么要用docker 问题 开发、测试、生产环境不统一&#xff0c;造成项目测试、部署时产生问题 解决方案 使用容器化技术&#xff0c;将环境和项目一起发送给测试、部署人员&#xff0c;测试人数和运维人员直接使用发过 来的环境和项目进行操作&#xff0c;避免环境不统一…

【卡梅德生物】单B细胞技术:牛单抗制备

1.牛单B细胞技术原理 单个B细胞抗体制备技术是近年来新发展的一类快速制备单克隆抗体的技术&#xff0c;基于流式细胞分选技术进行单B细胞单抗制备&#xff0c;利用每个B细胞只含有一个功能性重链可变区DNA序列和一个轻链可变区DNA序列且只产生一种特异性抗体的特性&#xff0c…

嵌入式linux 编译qt5(以v851s为例)

本文参考Blev大神的博客&#xff1a;Yuzuki Lizard V851S开发板 --移植 QT5.12.9教程&#xff08;群友Blev提供&#xff09; - Allwinner / 柚木PI-V851S - 嵌入式开发问答社区 (100ask.net) 一. 环境准备 1.下载qt5源码&#xff1a;Open Source Development | Open Source …

MathType绝对是我数学编辑的首选工具!

去年&#xff0c;微软曾说&#xff0c;要去掉Office里的公式编辑器&#xff0c;建议用户使用MathType编辑公式。目前Office用户可以到微软官网安装MathType的插件&#xff0c;现在免费使用&#xff0c;以后要收费。Word里安装这个插件以后&#xff0c;就会出现MathType的菜单。…

双交叉限幅控制总结

空燃比小于等于1使用下图&#xff1a;空燃比大于1&#xff0c;MAX变MIN&#xff0c;MIN变MAX&#xff0c;高低选互换即可。加负荷&#xff0c;先加空气&#xff0c;后加煤气&#xff0c;降负荷&#xff0c;先降煤气&#xff0c;后降空气。 交叉限幅和双交叉限幅的区别 交叉限幅…

磷酸铁锂电池生产污废水需要哪些工艺及设备

磷酸铁锂电池作为一种常见的锂离子电池&#xff0c;已广泛应用于电动汽车、储能系统等领域。然而&#xff0c;在磷酸铁锂电池的生产过程中&#xff0c;难免会产生一定量的污废水。为了有效处理和处理这些污废水&#xff0c;我们需要合适的工艺和设备。 首先&#xff0c;针对磷酸…

java期末复习题

1.任何一个Java程序都默认引入一个包&#xff0c;这个包的名字是________________。 正确答案&#xff1a;java.lang Java程序默认引入的包是java.lang包。这个包是Java语言的核心&#xff0c;它提供了Java中的基础类&#xff0c;包括基本Object类、Class类、String类、基本类…

react 项目结构配置

1 项目整体目录结构的搭建 如下图&#xff1a; 2 重置css样式: normalize.css reset.less ; 第一步 安装 npm i normalize.css 入口文件index.tsx导入&#xff1a;import ‘noremalize.css’ 第二步 创建自己的css样式&#xff1a;在assets文件夹中创建css…

【MySQL】:掌握SQL中DDL的数据库定义与操作

&#x1f3a5; 屿小夏 &#xff1a; 个人主页 &#x1f525;个人专栏 &#xff1a; MySQL从入门到进阶 &#x1f304; 莫道桑榆晚&#xff0c;为霞尚满天&#xff01; 文章目录 &#x1f4d1;前言一. SQL的分类二. DDL数据库操作2.1 查询所有数据库2.2 查询当前数据库2.3 创建数…

分类预测 | Matlab实现RP-LSTM-Attention递归图优化长短期记忆神经网络注意力机制的数据分类预测【24年新算法】

分类预测 | Matlab实现RP-LSTM-Attention递归图优化长短期记忆神经网络注意力机制的数据分类预测【24年新算法】 目录 分类预测 | Matlab实现RP-LSTM-Attention递归图优化长短期记忆神经网络注意力机制的数据分类预测【24年新算法】分类效果基本描述模型描述程序设计参考资料 分…

《设计模式的艺术》笔记 - 简单工厂模式

介绍 定义一个工厂类&#xff0c;它可以根据参数的不同返回不同类的实例&#xff0c;被创建的实例通常都具有相同的父类。因为在简单工厂模式中用于创建实例的方法是静态方法&#xff0c;因此简单工厂模式又被称为静态工厂方法模式&#xff0c;属于类创建型模式 实现 class Pr…

实现JavaScript的函数链式执行

本篇文章将会实现函数链式执行&#xff0c;链式步骤延时等操作。 1.工具类 const executor (name) > {/*** 执行器名称*/const executorName name;/*** 方法数组*/const methodArray [];/*** 同步执行某个方法*/function run(func){methodArray.push(func);return this…

谷歌aab包在Android 14闪退而apk没问题(targetsdk 34)

问题原因 Unity应用(target SDK 34)上线到GooglePlay&#xff0c;有用户反馈fold5设备上&#xff08;Android14系统&#xff09;疯狂闪退&#xff0c;经测试&#xff0c;在小米手机Android14系统的版本复现成功了&#xff0c;奇怪的是apk直接安装没问题&#xff0c;而打包成aa…

Android开发基础(二)

Android开发基础&#xff08;二&#xff09; 上篇主要描述了Android系统架构&#xff0c;代码是通过Java表示的&#xff1b; 本篇将从介绍Android组件去理解Android开发&#xff0c;代码将对Java和Kotlin进行对比。 Android组件 Android应用程序由一些零散的有联系的组件组成…

伴鱼离线数仓建设案例

伴鱼数仓建设案例 伴鱼离线数仓建立&#xff0c;与伴鱼的业务一起快速发展&#xff0c;从一条业务线&#xff0c;到多条业务线。在演进的过程中&#xff0c;有很多总结和沉淀的内容。本篇文章主要介绍伴鱼离线数据仓库的发展历史&#xff0c;在发展过程中遇到的各种问题&#…